1use core::ffi::c_void;
4use core::ptr::NonNull;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub enum ArenaError {
11 Failed,
13}
14
15pub struct Heap {
22 ptr: NonNull<rustfs_mimalloc_sys::mi_heap_t>,
23 owned: bool,
24}
25
26unsafe impl Send for Heap {}
27unsafe impl Sync for Heap {}
28
29impl Heap {
30 pub fn new() -> Option<Self> {
32 NonNull::new(unsafe { rustfs_mimalloc_sys::mi_heap_new() }).map(Self::owned)
33 }
34
35 pub fn new_in_arena(arena_id: ArenaId) -> Option<Self> {
37 NonNull::new(unsafe { rustfs_mimalloc_sys::mi_heap_new_in_arena(arena_id.0) })
38 .map(Self::owned)
39 }
40
41 pub fn main() -> Self {
43 let ptr = unsafe { rustfs_mimalloc_sys::mi_heap_main() };
44 Self::borrowed(NonNull::new(ptr).expect("mi_heap_main returned null"))
45 }
46
47 pub unsafe fn heap_of(ptr: *const u8) -> Option<Self> {
52 NonNull::new(unsafe { rustfs_mimalloc_sys::mi_heap_of(ptr as *const c_void) })
53 .map(Self::borrowed)
54 }
55
56 pub unsafe fn contains(&self, ptr: *const u8) -> bool {
61 unsafe { rustfs_mimalloc_sys::mi_heap_contains(self.ptr.as_ptr(), ptr as *const c_void) }
62 }
63
64 pub unsafe fn malloc(&self, size: usize) -> *mut u8 {
69 unsafe { rustfs_mimalloc_sys::mi_heap_malloc(self.ptr.as_ptr(), size) as *mut u8 }
70 }
71
72 pub unsafe fn zalloc(&self, size: usize) -> *mut u8 {
77 unsafe { rustfs_mimalloc_sys::mi_heap_zalloc(self.ptr.as_ptr(), size) as *mut u8 }
78 }
79
80 pub unsafe fn malloc_aligned(&self, size: usize, alignment: usize) -> *mut u8 {
85 unsafe {
86 rustfs_mimalloc_sys::mi_heap_malloc_aligned(self.ptr.as_ptr(), size, alignment)
87 as *mut u8
88 }
89 }
90
91 pub unsafe fn realloc(&self, ptr: *mut u8, new_size: usize) -> *mut u8 {
96 unsafe {
97 rustfs_mimalloc_sys::mi_heap_realloc(self.ptr.as_ptr(), ptr as *mut c_void, new_size)
98 as *mut u8
99 }
100 }
101
102 pub fn delete(self) {
105 if self.owned {
106 unsafe { rustfs_mimalloc_sys::mi_heap_delete(self.ptr.as_ptr()) }
107 }
108 core::mem::forget(self);
109 }
110
111 pub unsafe fn destroy(self) {
116 if self.owned {
117 unsafe { rustfs_mimalloc_sys::mi_heap_destroy(self.ptr.as_ptr()) };
118 }
119 core::mem::forget(self);
120 }
121
122 pub fn collect(&self, force: bool) {
124 unsafe { rustfs_mimalloc_sys::mi_heap_collect(self.ptr.as_ptr(), force) }
125 }
126
127 pub fn stats_json(&self) -> String {
129 unsafe {
130 crate::ffi::owned_mimalloc_string(rustfs_mimalloc_sys::mi_heap_stats_get_json(
131 self.ptr.as_ptr(),
132 0,
133 core::ptr::null_mut(),
134 ))
135 }
136 }
137
138 pub fn stats_print(&self) -> String {
140 crate::ffi::collect_mimalloc_output(|out, arg| unsafe {
141 rustfs_mimalloc_sys::mi_heap_stats_print_out(self.ptr.as_ptr(), out, arg);
142 })
143 }
144
145 pub fn as_ptr(&self) -> *mut rustfs_mimalloc_sys::mi_heap_t {
147 self.ptr.as_ptr()
148 }
149
150 #[inline]
151 fn owned(ptr: NonNull<rustfs_mimalloc_sys::mi_heap_t>) -> Self {
152 Self { ptr, owned: true }
153 }
154
155 #[inline]
156 fn borrowed(ptr: NonNull<rustfs_mimalloc_sys::mi_heap_t>) -> Self {
157 Self { ptr, owned: false }
158 }
159}
160
161impl Drop for Heap {
162 fn drop(&mut self) {
163 if self.owned {
164 unsafe { rustfs_mimalloc_sys::mi_heap_delete(self.ptr.as_ptr()) }
165 }
166 }
167}
168
169#[derive(Debug, Clone, Copy)]
173pub struct ArenaId(rustfs_mimalloc_sys::mi_arena_id_t);
174
175unsafe impl Send for ArenaId {}
176unsafe impl Sync for ArenaId {}
177
178pub fn reserve_os_memory(
180 size: usize,
181 commit: bool,
182 allow_large: bool,
183 exclusive: bool,
184) -> Result<ArenaId, ArenaError> {
185 let mut id = core::ptr::null_mut();
186 let rc = unsafe {
187 rustfs_mimalloc_sys::mi_reserve_os_memory_ex(size, commit, allow_large, exclusive, &mut id)
188 };
189 if rc == 0 {
190 Ok(ArenaId(id))
191 } else {
192 Err(ArenaError::Failed)
193 }
194}
195
196pub unsafe fn manage_os_memory(
201 start: *mut u8,
202 size: usize,
203 is_committed: bool,
204 is_pinned: bool,
205 is_zero: bool,
206 numa_node: i32,
207 exclusive: bool,
208) -> Result<ArenaId, ArenaError> {
209 let mut id = core::ptr::null_mut();
210 let ok = unsafe {
211 rustfs_mimalloc_sys::mi_manage_os_memory_ex(
212 start as *mut c_void,
213 size,
214 is_committed,
215 is_pinned,
216 is_zero,
217 numa_node,
218 exclusive,
219 &mut id,
220 )
221 };
222 if ok {
223 Ok(ArenaId(id))
224 } else {
225 Err(ArenaError::Failed)
226 }
227}
228
229#[inline]
231pub fn arena_min_alignment() -> usize {
232 unsafe { rustfs_mimalloc_sys::mi_arena_min_alignment() }
233}
234
235#[inline]
237pub fn arena_min_size() -> usize {
238 unsafe { rustfs_mimalloc_sys::mi_arena_min_size() }
239}
240
241#[inline]
243pub fn arena_max_object_size() -> usize {
244 unsafe { rustfs_mimalloc_sys::mi_arena_max_object_size() }
245}
246
247#[cfg(test)]
250mod tests {
251 use super::*;
252
253 #[test]
254 fn heap_create_delete() {
255 let heap = Heap::new().expect("heap::new failed");
256 heap.delete();
257 }
258
259 #[test]
260 fn heap_alloc_free() {
261 let heap = Heap::new().unwrap();
262 unsafe {
263 let ptr = heap.malloc(128);
264 assert!(!ptr.is_null());
265 core::ptr::write_bytes(ptr, 0xCD, 128);
266 rustfs_mimalloc_sys::mi_free(ptr as *mut c_void);
267 }
268 heap.delete();
269 }
270
271 #[test]
272 fn heap_aligned_alloc() {
273 let heap = Heap::new().unwrap();
274 unsafe {
275 for pow in 0..=12 {
276 let align = 1usize << pow;
277 let ptr = heap.malloc_aligned(64, align);
278 assert!(!ptr.is_null());
279 assert_eq!(ptr as usize % align, 0, "align={align}");
280 rustfs_mimalloc_sys::mi_free(ptr as *mut c_void);
281 }
282 }
283 heap.delete();
284 }
285
286 #[test]
287 fn arena_min_values_are_sane() {
288 let a = arena_min_alignment();
289 assert!(a > 0 && a.is_power_of_two());
290 }
291
292 #[test]
293 fn heap_stats_are_available() {
294 let heap = Heap::new().unwrap();
295 assert!(!heap.stats_json().is_empty());
296 assert!(!heap.stats_print().is_empty());
297 heap.delete();
298 }
299
300 #[test]
301 fn borrowed_main_heap_delete_is_noop() {
302 let heap = Heap::main();
303 heap.delete();
304
305 unsafe {
306 let ptr = rustfs_mimalloc_sys::mi_malloc(64);
307 assert!(!ptr.is_null());
308 rustfs_mimalloc_sys::mi_free(ptr);
309 }
310 }
311}