use {
libc::{c_void, size_t},
python3_sys as pyffi,
std::alloc,
std::collections::HashMap,
};
#[cfg(feature = "jemalloc-sys")]
use {jemalloc_sys as jemallocffi, std::ptr::null_mut};
const MIN_ALIGN: usize = 16;
type RawAllocatorState = HashMap<*mut u8, alloc::Layout>;
pub struct RawAllocator {
pub allocator: pyffi::PyMemAllocatorEx,
_state: Box<RawAllocatorState>,
}
extern "C" fn raw_rust_malloc(ctx: *mut c_void, size: size_t) -> *mut c_void {
let size = match size {
0 => 1,
val => val,
};
unsafe {
let state = ctx as *mut RawAllocatorState;
let layout = alloc::Layout::from_size_align_unchecked(size, MIN_ALIGN);
let res = alloc::alloc(layout);
(*state).insert(res, layout);
res as *mut c_void
}
}
extern "C" fn raw_rust_calloc(ctx: *mut c_void, nelem: size_t, elsize: size_t) -> *mut c_void {
let size = match nelem * elsize {
0 => 1,
val => val,
};
unsafe {
let state = ctx as *mut RawAllocatorState;
let layout = alloc::Layout::from_size_align_unchecked(size, MIN_ALIGN);
let res = alloc::alloc_zeroed(layout);
(*state).insert(res, layout);
res as *mut c_void
}
}
extern "C" fn raw_rust_realloc(
ctx: *mut c_void,
ptr: *mut c_void,
new_size: size_t,
) -> *mut c_void {
if ptr.is_null() {
return raw_rust_malloc(ctx, new_size);
}
let new_size = match new_size {
0 => 1,
val => val,
};
unsafe {
let state = ctx as *mut RawAllocatorState;
let layout = alloc::Layout::from_size_align_unchecked(new_size, MIN_ALIGN);
let key = ptr as *mut u8;
let old_layout = (*state)
.remove(&key)
.expect("original memory address not tracked");
let res = alloc::realloc(ptr as *mut u8, old_layout, new_size);
(*state).insert(res, layout);
res as *mut c_void
}
}
extern "C" fn raw_rust_free(ctx: *mut c_void, ptr: *mut c_void) {
if ptr.is_null() {
return;
}
unsafe {
let state = ctx as *mut RawAllocatorState;
let key = ptr as *mut u8;
let layout = (*state)
.get(&key)
.unwrap_or_else(|| panic!("could not find allocated memory record: {:?}", key));
alloc::dealloc(key, *layout);
(*state).remove(&key);
}
}
pub fn make_raw_rust_memory_allocator() -> RawAllocator {
let alloc = Box::new(HashMap::<*mut u8, alloc::Layout>::new());
let state = Box::into_raw(alloc);
let allocator = pyffi::PyMemAllocatorEx {
ctx: state as *mut c_void,
malloc: Some(raw_rust_malloc),
calloc: Some(raw_rust_calloc),
realloc: Some(raw_rust_realloc),
free: Some(raw_rust_free),
};
RawAllocator {
allocator,
_state: unsafe { Box::from_raw(state) },
}
}
#[cfg(feature = "jemalloc-sys")]
extern "C" fn raw_jemalloc_malloc(_ctx: *mut c_void, size: size_t) -> *mut c_void {
let size = match size {
0 => 1,
val => val,
};
unsafe { jemallocffi::mallocx(size, 0) }
}
#[cfg(feature = "jemalloc-sys")]
extern "C" fn raw_jemalloc_calloc(_ctx: *mut c_void, nelem: size_t, elsize: size_t) -> *mut c_void {
let size = match nelem * elsize {
0 => 1,
val => val,
};
unsafe { jemallocffi::mallocx(size, jemallocffi::MALLOCX_ZERO) }
}
#[cfg(feature = "jemalloc-sys")]
extern "C" fn raw_jemalloc_realloc(
ctx: *mut c_void,
ptr: *mut c_void,
new_size: size_t,
) -> *mut c_void {
if ptr.is_null() {
return raw_jemalloc_malloc(ctx, new_size);
}
let new_size = match new_size {
0 => 1,
val => val,
};
unsafe { jemallocffi::rallocx(ptr, new_size, 0) }
}
#[cfg(feature = "jemalloc-sys")]
extern "C" fn raw_jemalloc_free(_ctx: *mut c_void, ptr: *mut c_void) {
if ptr.is_null() {
return;
}
unsafe { jemallocffi::dallocx(ptr, 0) }
}
#[cfg(feature = "jemalloc-sys")]
pub fn make_raw_jemalloc_allocator() -> pyffi::PyMemAllocatorEx {
pyffi::PyMemAllocatorEx {
ctx: null_mut(),
malloc: Some(raw_jemalloc_malloc),
calloc: Some(raw_jemalloc_calloc),
realloc: Some(raw_jemalloc_realloc),
free: Some(raw_jemalloc_free),
}
}