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