1mod api;
11mod ffi;
12
13pub mod heap;
14
15pub use api::{ProcessInfo, set_current_thread_in_threadpool};
16pub use rustfs_mimalloc_sys::{MI_SMALL_SIZE_MAX, MI_SMALL_WSIZE_MAX};
17
18use core::alloc::{GlobalAlloc, Layout};
19use core::ffi::c_void;
20
21#[derive(Debug, Clone, Copy, Default)]
26pub struct MiMalloc;
27
28unsafe impl GlobalAlloc for MiMalloc {
31 #[inline(always)]
32 unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
33 unsafe { rustfs_mimalloc_sys::mi_malloc_aligned(layout.size(), layout.align()) as *mut u8 }
34 }
35
36 #[inline(always)]
37 unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
38 unsafe { rustfs_mimalloc_sys::mi_zalloc_aligned(layout.size(), layout.align()) as *mut u8 }
39 }
40
41 #[inline(always)]
42 unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {
43 unsafe { rustfs_mimalloc_sys::mi_free(ptr as *mut c_void) };
44 }
45
46 #[inline(always)]
47 unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
48 unsafe {
49 rustfs_mimalloc_sys::mi_realloc_aligned(ptr as *mut c_void, new_size, layout.align())
50 as *mut u8
51 }
52 }
53}
54
55#[cfg(test)]
58mod tests {
59 use super::*;
60 use std::alloc::{GlobalAlloc, Layout};
61
62 #[global_allocator]
63 static GLOBAL: MiMalloc = MiMalloc;
64
65 #[test]
66 fn alloc_dealloc_roundtrip() {
67 let layout = Layout::from_size_align(64, 8).unwrap();
68 unsafe {
69 let ptr = GLOBAL.alloc(layout);
70 assert!(!ptr.is_null());
71 GLOBAL.dealloc(ptr, layout);
72 }
73 }
74
75 #[test]
76 fn alloc_zeroed_is_zero() {
77 let layout = Layout::from_size_align(256, 16).unwrap();
78 unsafe {
79 let ptr = GLOBAL.alloc_zeroed(layout);
80 assert!(!ptr.is_null());
81 assert!((0..256).all(|i| *ptr.add(i) == 0));
82 GLOBAL.dealloc(ptr, layout);
83 }
84 }
85
86 #[test]
87 fn realloc_preserves_content() {
88 let layout = Layout::from_size_align(64, 8).unwrap();
89 unsafe {
90 let ptr = GLOBAL.alloc(layout);
91 core::ptr::write_bytes(ptr, 0xAB, 64);
92 let new_ptr = GLOBAL.realloc(ptr, layout, 128);
93 assert!(!new_ptr.is_null());
94 assert!((0..64).all(|i| *new_ptr.add(i) == 0xAB));
95 GLOBAL.dealloc(new_ptr, Layout::from_size_align(128, 8).unwrap());
96 }
97 }
98
99 #[test]
100 fn alignment_respected() {
101 for align_pow in 0..=12 {
102 let align = 1usize << align_pow;
103 let layout = Layout::from_size_align(64, align).unwrap();
104 unsafe {
105 let ptr = GLOBAL.alloc(layout);
106 assert!(!ptr.is_null(), "align={align}");
107 assert_eq!(ptr as usize % align, 0, "align={align}");
108 GLOBAL.dealloc(ptr, layout);
109 }
110 }
111 }
112
113 #[test]
114 fn large_alignment_page_size() {
115 let layout = Layout::from_size_align(4096, 4096).unwrap();
117 for _ in 0..100 {
118 unsafe {
119 let ptr = GLOBAL.alloc(layout);
120 assert!(!ptr.is_null());
121 assert_eq!(ptr as usize % 4096, 0);
122 GLOBAL.dealloc(ptr, layout);
123 }
124 }
125 }
126
127 #[test]
128 fn vec_push_smoke() {
129 let v: Vec<i32> = (0..10_000).collect();
130 assert_eq!(v.len(), 10_000);
131 assert_eq!(v[9999], 9999);
132 }
133
134 #[test]
135 fn box_smoke() {
136 let b = Box::new(42u64);
137 assert_eq!(*b, 42);
138 }
139
140 #[test]
141 fn concurrent_alloc_free() {
142 use std::sync::{Arc, Barrier};
143 use std::thread;
144
145 let n = 8;
146 let barrier = Arc::new(Barrier::new(n));
147 let handles: Vec<_> = (0..n)
148 .map(|_| {
149 let b = barrier.clone();
150 thread::spawn(move || {
151 b.wait();
152 let layout = Layout::from_size_align(128, 16).unwrap();
153 for _ in 0..1000 {
154 unsafe {
155 let ptr = GLOBAL.alloc(layout);
156 assert!(!ptr.is_null());
157 core::ptr::write_bytes(ptr, 0xCD, 128);
158 GLOBAL.dealloc(ptr, layout);
159 }
160 }
161 })
162 })
163 .collect();
164 for h in handles {
165 h.join().unwrap();
166 }
167 }
168}