malloc_best_effort/
lib.rs

1#![no_std]
2#![cfg_attr(docsrs, feature(doc_cfg))]
3
4//! Best effort memory allocator for Rust
5//!
6//! Selects suitable allocator depending on target OS and architecture.
7//!
8//! ## Usage
9//! ```rust,ignore
10//! use malloc_best_effort::BEMalloc;
11//!
12//! #[global_allocator]
13//! static GLOBAL: BEMalloc = BEMalloc::new();
14//! ```
15//! ## Feature flags
16#![doc = document_features::document_features!()]
17
18#[cfg(not(all(
19    target_os = "linux",
20    any(target_arch = "x86_64", target_arch = "aarch64")
21)))]
22mod mimalloc {
23    pub(crate) use mimalloc::MiMalloc as BEMallocImpl;
24
25    #[inline]
26    pub(crate) fn init_impl() {}
27}
28#[cfg(all(
29    target_os = "linux",
30    any(target_arch = "x86_64", target_arch = "aarch64")
31))]
32mod tcmalloc {
33    pub(crate) use tcmalloc_better::TCMalloc as BEMallocImpl;
34
35    #[cfg(feature = "std")]
36    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
37    #[inline]
38    pub(crate) fn init_impl() {
39        BEMallocImpl::process_background_actions_thread();
40    }
41
42    #[cfg(not(feature = "std"))]
43    #[cfg_attr(docsrs, doc(cfg(not(feature = "std"))))]
44    #[inline]
45    pub(crate) fn init_impl() {}
46}
47
48#[cfg(not(all(
49    target_os = "linux",
50    any(target_arch = "x86_64", target_arch = "aarch64")
51)))]
52use crate::mimalloc::BEMallocImpl;
53#[cfg(not(all(
54    target_os = "linux",
55    any(target_arch = "x86_64", target_arch = "aarch64")
56)))]
57use crate::mimalloc::init_impl;
58#[cfg(all(
59    target_os = "linux",
60    any(target_arch = "x86_64", target_arch = "aarch64")
61))]
62use crate::tcmalloc::BEMallocImpl;
63#[cfg(all(
64    target_os = "linux",
65    any(target_arch = "x86_64", target_arch = "aarch64")
66))]
67use crate::tcmalloc::init_impl;
68use core::alloc::{GlobalAlloc, Layout};
69
70/// A memory allocator that can be registered as the standard library’s default
71/// through the `#[global_allocator]` attribute.
72pub struct BEMalloc {
73    alloc_impl: BEMallocImpl,
74}
75
76unsafe impl GlobalAlloc for BEMalloc {
77    #[inline]
78    unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
79        unsafe { self.alloc_impl.alloc(layout) }
80    }
81
82    #[inline]
83    unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
84        unsafe { self.alloc_impl.dealloc(ptr, layout) }
85    }
86
87    #[inline]
88    unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
89        unsafe { self.alloc_impl.alloc_zeroed(layout) }
90    }
91
92    #[inline]
93    unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
94        unsafe { self.alloc_impl.realloc(ptr, layout, new_size) }
95    }
96}
97
98impl Default for BEMalloc {
99    #[inline]
100    fn default() -> Self {
101        Self::new()
102    }
103}
104
105impl BEMalloc {
106    /// Create new instance of allocator.
107    #[inline]
108    pub const fn new() -> Self {
109        Self {
110            alloc_impl: BEMallocImpl,
111        }
112    }
113    /// Start allocator background job. Should be called in `main()` function.
114    #[inline]
115    pub fn init() {
116        init_impl();
117    }
118}
119
120#[cfg(test)]
121mod tests {
122    use super::*;
123
124    #[test]
125    fn it_frees_allocated_memory() {
126        unsafe {
127            let layout = Layout::from_size_align(8, 8).unwrap();
128            let alloc = BEMalloc::new();
129
130            let ptr = alloc.alloc(layout);
131            alloc.dealloc(ptr, layout);
132        }
133    }
134
135    #[test]
136    fn it_frees_allocated_big_memory() {
137        unsafe {
138            let layout = Layout::from_size_align(1 << 20, 32).unwrap();
139            let alloc = BEMalloc::new();
140
141            let ptr = alloc.alloc(layout);
142            alloc.dealloc(ptr, layout);
143        }
144    }
145
146    #[test]
147    fn it_frees_zero_allocated_memory() {
148        unsafe {
149            let layout = Layout::from_size_align(8, 8).unwrap();
150            let alloc = BEMalloc::new();
151
152            let ptr = alloc.alloc_zeroed(layout);
153            alloc.dealloc(ptr, layout);
154        }
155    }
156
157    #[test]
158    fn it_frees_zero_allocated_big_memory() {
159        unsafe {
160            let layout = Layout::from_size_align(1 << 20, 32).unwrap();
161            let alloc = BEMalloc::new();
162
163            let ptr = alloc.alloc_zeroed(layout);
164            alloc.dealloc(ptr, layout);
165        }
166    }
167
168    #[test]
169    fn it_frees_reallocated_memory() {
170        unsafe {
171            let layout = Layout::from_size_align(8, 8).unwrap();
172            let new_size = 16;
173            let new_layout = Layout::from_size_align(new_size, layout.align()).unwrap();
174            let alloc = BEMalloc::new();
175
176            let ptr = alloc.alloc(layout);
177            let ptr = alloc.realloc(ptr, layout, new_size);
178            alloc.dealloc(ptr, new_layout);
179        }
180    }
181
182    #[test]
183    fn it_frees_reallocated_big_memory() {
184        unsafe {
185            let layout = Layout::from_size_align(1 << 20, 32).unwrap();
186            let new_size = 2 << 20;
187            let new_layout = Layout::from_size_align(new_size, layout.align()).unwrap();
188            let alloc = BEMalloc::new();
189
190            let ptr = alloc.alloc(layout);
191            let ptr = alloc.realloc(ptr, layout, new_size);
192            alloc.dealloc(ptr, new_layout);
193        }
194    }
195}