use core::ffi::c_void;
use core::ptr::NonNull;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ArenaError {
Failed,
}
pub struct Heap {
ptr: NonNull<rustfs_mimalloc_sys::mi_heap_t>,
owned: bool,
}
unsafe impl Send for Heap {}
unsafe impl Sync for Heap {}
impl Heap {
pub fn new() -> Option<Self> {
NonNull::new(unsafe { rustfs_mimalloc_sys::mi_heap_new() }).map(Self::owned)
}
pub fn new_in_arena(arena_id: ArenaId) -> Option<Self> {
NonNull::new(unsafe { rustfs_mimalloc_sys::mi_heap_new_in_arena(arena_id.0) })
.map(Self::owned)
}
pub fn main() -> Self {
let ptr = unsafe { rustfs_mimalloc_sys::mi_heap_main() };
Self::borrowed(NonNull::new(ptr).expect("mi_heap_main returned null"))
}
pub unsafe fn heap_of(ptr: *const u8) -> Option<Self> {
NonNull::new(unsafe { rustfs_mimalloc_sys::mi_heap_of(ptr as *const c_void) })
.map(Self::borrowed)
}
pub unsafe fn contains(&self, ptr: *const u8) -> bool {
unsafe { rustfs_mimalloc_sys::mi_heap_contains(self.ptr.as_ptr(), ptr as *const c_void) }
}
pub unsafe fn malloc(&self, size: usize) -> *mut u8 {
unsafe { rustfs_mimalloc_sys::mi_heap_malloc(self.ptr.as_ptr(), size) as *mut u8 }
}
pub unsafe fn zalloc(&self, size: usize) -> *mut u8 {
unsafe { rustfs_mimalloc_sys::mi_heap_zalloc(self.ptr.as_ptr(), size) as *mut u8 }
}
pub unsafe fn malloc_aligned(&self, size: usize, alignment: usize) -> *mut u8 {
unsafe {
rustfs_mimalloc_sys::mi_heap_malloc_aligned(self.ptr.as_ptr(), size, alignment)
as *mut u8
}
}
pub unsafe fn realloc(&self, ptr: *mut u8, new_size: usize) -> *mut u8 {
unsafe {
rustfs_mimalloc_sys::mi_heap_realloc(self.ptr.as_ptr(), ptr as *mut c_void, new_size)
as *mut u8
}
}
pub fn delete(self) {
if self.owned {
unsafe { rustfs_mimalloc_sys::mi_heap_delete(self.ptr.as_ptr()) }
}
core::mem::forget(self);
}
pub unsafe fn destroy(self) {
if self.owned {
unsafe { rustfs_mimalloc_sys::mi_heap_destroy(self.ptr.as_ptr()) };
}
core::mem::forget(self);
}
pub fn collect(&self, force: bool) {
unsafe { rustfs_mimalloc_sys::mi_heap_collect(self.ptr.as_ptr(), force) }
}
pub fn stats_json(&self) -> String {
unsafe {
crate::ffi::owned_mimalloc_string(rustfs_mimalloc_sys::mi_heap_stats_get_json(
self.ptr.as_ptr(),
0,
core::ptr::null_mut(),
))
}
}
pub fn stats_print(&self) -> String {
crate::ffi::collect_mimalloc_output(|out, arg| unsafe {
rustfs_mimalloc_sys::mi_heap_stats_print_out(self.ptr.as_ptr(), out, arg);
})
}
pub fn as_ptr(&self) -> *mut rustfs_mimalloc_sys::mi_heap_t {
self.ptr.as_ptr()
}
#[inline]
fn owned(ptr: NonNull<rustfs_mimalloc_sys::mi_heap_t>) -> Self {
Self { ptr, owned: true }
}
#[inline]
fn borrowed(ptr: NonNull<rustfs_mimalloc_sys::mi_heap_t>) -> Self {
Self { ptr, owned: false }
}
}
impl Drop for Heap {
fn drop(&mut self) {
if self.owned {
unsafe { rustfs_mimalloc_sys::mi_heap_delete(self.ptr.as_ptr()) }
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct ArenaId(rustfs_mimalloc_sys::mi_arena_id_t);
unsafe impl Send for ArenaId {}
unsafe impl Sync for ArenaId {}
pub fn reserve_os_memory(
size: usize,
commit: bool,
allow_large: bool,
exclusive: bool,
) -> Result<ArenaId, ArenaError> {
let mut id = core::ptr::null_mut();
let rc = unsafe {
rustfs_mimalloc_sys::mi_reserve_os_memory_ex(size, commit, allow_large, exclusive, &mut id)
};
if rc == 0 {
Ok(ArenaId(id))
} else {
Err(ArenaError::Failed)
}
}
pub unsafe fn manage_os_memory(
start: *mut u8,
size: usize,
is_committed: bool,
is_pinned: bool,
is_zero: bool,
numa_node: i32,
exclusive: bool,
) -> Result<ArenaId, ArenaError> {
let mut id = core::ptr::null_mut();
let ok = unsafe {
rustfs_mimalloc_sys::mi_manage_os_memory_ex(
start as *mut c_void,
size,
is_committed,
is_pinned,
is_zero,
numa_node,
exclusive,
&mut id,
)
};
if ok {
Ok(ArenaId(id))
} else {
Err(ArenaError::Failed)
}
}
#[inline]
pub fn arena_min_alignment() -> usize {
unsafe { rustfs_mimalloc_sys::mi_arena_min_alignment() }
}
#[inline]
pub fn arena_min_size() -> usize {
unsafe { rustfs_mimalloc_sys::mi_arena_min_size() }
}
#[inline]
pub fn arena_max_object_size() -> usize {
unsafe { rustfs_mimalloc_sys::mi_arena_max_object_size() }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn heap_create_delete() {
let heap = Heap::new().expect("heap::new failed");
heap.delete();
}
#[test]
fn heap_alloc_free() {
let heap = Heap::new().unwrap();
unsafe {
let ptr = heap.malloc(128);
assert!(!ptr.is_null());
core::ptr::write_bytes(ptr, 0xCD, 128);
rustfs_mimalloc_sys::mi_free(ptr as *mut c_void);
}
heap.delete();
}
#[test]
fn heap_aligned_alloc() {
let heap = Heap::new().unwrap();
unsafe {
for pow in 0..=12 {
let align = 1usize << pow;
let ptr = heap.malloc_aligned(64, align);
assert!(!ptr.is_null());
assert_eq!(ptr as usize % align, 0, "align={align}");
rustfs_mimalloc_sys::mi_free(ptr as *mut c_void);
}
}
heap.delete();
}
#[test]
fn arena_min_values_are_sane() {
let a = arena_min_alignment();
assert!(a > 0 && a.is_power_of_two());
}
#[test]
fn heap_stats_are_available() {
let heap = Heap::new().unwrap();
assert!(!heap.stats_json().is_empty());
assert!(!heap.stats_print().is_empty());
heap.delete();
}
#[test]
fn borrowed_main_heap_delete_is_noop() {
let heap = Heap::main();
heap.delete();
unsafe {
let ptr = rustfs_mimalloc_sys::mi_malloc(64);
assert!(!ptr.is_null());
rustfs_mimalloc_sys::mi_free(ptr);
}
}
}