1 | use crate::alloc::Layout; |
2 | use crate::cmp; |
3 | use crate::ptr; |
4 | |
5 | /// A memory allocator that can be registered as the standard library’s default |
6 | /// through the `#[global_allocator]` attribute. |
7 | /// |
8 | /// Some of the methods require that a memory block be *currently |
9 | /// allocated* via an allocator. This means that: |
10 | /// |
11 | /// * the starting address for that memory block was previously |
12 | /// returned by a previous call to an allocation method |
13 | /// such as `alloc`, and |
14 | /// |
15 | /// * the memory block has not been subsequently deallocated, where |
16 | /// blocks are deallocated either by being passed to a deallocation |
17 | /// method such as `dealloc` or by being |
18 | /// passed to a reallocation method that returns a non-null pointer. |
19 | /// |
20 | /// |
21 | /// # Example |
22 | /// |
23 | /// ``` |
24 | /// use std::alloc::{GlobalAlloc, Layout}; |
25 | /// use std::cell::UnsafeCell; |
26 | /// use std::ptr::null_mut; |
27 | /// use std::sync::atomic::{ |
28 | /// AtomicUsize, |
29 | /// Ordering::{Acquire, SeqCst}, |
30 | /// }; |
31 | /// |
32 | /// const ARENA_SIZE: usize = 128 * 1024; |
33 | /// const MAX_SUPPORTED_ALIGN: usize = 4096; |
34 | /// #[repr(C, align(4096))] // 4096 == MAX_SUPPORTED_ALIGN |
35 | /// struct SimpleAllocator { |
36 | /// arena: UnsafeCell<[u8; ARENA_SIZE]>, |
37 | /// remaining: AtomicUsize, // we allocate from the top, counting down |
38 | /// } |
39 | /// |
40 | /// #[global_allocator] |
41 | /// static ALLOCATOR: SimpleAllocator = SimpleAllocator { |
42 | /// arena: UnsafeCell::new([0x55; ARENA_SIZE]), |
43 | /// remaining: AtomicUsize::new(ARENA_SIZE), |
44 | /// }; |
45 | /// |
46 | /// unsafe impl Sync for SimpleAllocator {} |
47 | /// |
48 | /// unsafe impl GlobalAlloc for SimpleAllocator { |
49 | /// unsafe fn alloc(&self, layout: Layout) -> *mut u8 { |
50 | /// let size = layout.size(); |
51 | /// let align = layout.align(); |
52 | /// |
53 | /// // `Layout` contract forbids making a `Layout` with align=0, or align not power of 2. |
54 | /// // So we can safely use a mask to ensure alignment without worrying about UB. |
55 | /// let align_mask_to_round_down = !(align - 1); |
56 | /// |
57 | /// if align > MAX_SUPPORTED_ALIGN { |
58 | /// return null_mut(); |
59 | /// } |
60 | /// |
61 | /// let mut allocated = 0; |
62 | /// if self |
63 | /// .remaining |
64 | /// .fetch_update(SeqCst, SeqCst, |mut remaining| { |
65 | /// if size > remaining { |
66 | /// return None; |
67 | /// } |
68 | /// remaining -= size; |
69 | /// remaining &= align_mask_to_round_down; |
70 | /// allocated = remaining; |
71 | /// Some(remaining) |
72 | /// }) |
73 | /// .is_err() |
74 | /// { |
75 | /// return null_mut(); |
76 | /// }; |
77 | /// self.arena.get().cast::<u8>().add(allocated) |
78 | /// } |
79 | /// unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {} |
80 | /// } |
81 | /// |
82 | /// fn main() { |
83 | /// let _s = format!("allocating a string!" ); |
84 | /// let currently = ALLOCATOR.remaining.load(Acquire); |
85 | /// println!("allocated so far: {}" , ARENA_SIZE - currently); |
86 | /// } |
87 | /// ``` |
88 | /// |
89 | /// # Safety |
90 | /// |
91 | /// The `GlobalAlloc` trait is an `unsafe` trait for a number of reasons, and |
92 | /// implementors must ensure that they adhere to these contracts: |
93 | /// |
94 | /// * It's undefined behavior if global allocators unwind. This restriction may |
95 | /// be lifted in the future, but currently a panic from any of these |
96 | /// functions may lead to memory unsafety. |
97 | /// |
98 | /// * `Layout` queries and calculations in general must be correct. Callers of |
99 | /// this trait are allowed to rely on the contracts defined on each method, |
100 | /// and implementors must ensure such contracts remain true. |
101 | /// |
102 | /// * You must not rely on allocations actually happening, even if there are explicit |
103 | /// heap allocations in the source. The optimizer may detect unused allocations that it can either |
104 | /// eliminate entirely or move to the stack and thus never invoke the allocator. The |
105 | /// optimizer may further assume that allocation is infallible, so code that used to fail due |
106 | /// to allocator failures may now suddenly work because the optimizer worked around the |
107 | /// need for an allocation. More concretely, the following code example is unsound, irrespective |
108 | /// of whether your custom allocator allows counting how many allocations have happened. |
109 | /// |
110 | /// ```rust,ignore (unsound and has placeholders) |
111 | /// drop(Box::new(42)); |
112 | /// let number_of_heap_allocs = /* call private allocator API */; |
113 | /// unsafe { std::hint::assert_unchecked(number_of_heap_allocs > 0); } |
114 | /// ``` |
115 | /// |
116 | /// Note that the optimizations mentioned above are not the only |
117 | /// optimization that can be applied. You may generally not rely on heap allocations |
118 | /// happening if they can be removed without changing program behavior. |
119 | /// Whether allocations happen or not is not part of the program behavior, even if it |
120 | /// could be detected via an allocator that tracks allocations by printing or otherwise |
121 | /// having side effects. |
122 | #[stable (feature = "global_alloc" , since = "1.28.0" )] |
123 | pub unsafe trait GlobalAlloc { |
124 | /// Allocate memory as described by the given `layout`. |
125 | /// |
126 | /// Returns a pointer to newly-allocated memory, |
127 | /// or null to indicate allocation failure. |
128 | /// |
129 | /// # Safety |
130 | /// |
131 | /// This function is unsafe because undefined behavior can result |
132 | /// if the caller does not ensure that `layout` has non-zero size. |
133 | /// |
134 | /// (Extension subtraits might provide more specific bounds on |
135 | /// behavior, e.g., guarantee a sentinel address or a null pointer |
136 | /// in response to a zero-size allocation request.) |
137 | /// |
138 | /// The allocated block of memory may or may not be initialized. |
139 | /// |
140 | /// # Errors |
141 | /// |
142 | /// Returning a null pointer indicates that either memory is exhausted |
143 | /// or `layout` does not meet this allocator's size or alignment constraints. |
144 | /// |
145 | /// Implementations are encouraged to return null on memory |
146 | /// exhaustion rather than aborting, but this is not |
147 | /// a strict requirement. (Specifically: it is *legal* to |
148 | /// implement this trait atop an underlying native allocation |
149 | /// library that aborts on memory exhaustion.) |
150 | /// |
151 | /// Clients wishing to abort computation in response to an |
152 | /// allocation error are encouraged to call the [`handle_alloc_error`] function, |
153 | /// rather than directly invoking `panic!` or similar. |
154 | /// |
155 | /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html |
156 | #[stable (feature = "global_alloc" , since = "1.28.0" )] |
157 | unsafe fn alloc(&self, layout: Layout) -> *mut u8; |
158 | |
159 | /// Deallocate the block of memory at the given `ptr` pointer with the given `layout`. |
160 | /// |
161 | /// # Safety |
162 | /// |
163 | /// This function is unsafe because undefined behavior can result |
164 | /// if the caller does not ensure all of the following: |
165 | /// |
166 | /// * `ptr` must denote a block of memory currently allocated via |
167 | /// this allocator, |
168 | /// |
169 | /// * `layout` must be the same layout that was used |
170 | /// to allocate that block of memory. |
171 | #[stable (feature = "global_alloc" , since = "1.28.0" )] |
172 | unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout); |
173 | |
174 | /// Behaves like `alloc`, but also ensures that the contents |
175 | /// are set to zero before being returned. |
176 | /// |
177 | /// # Safety |
178 | /// |
179 | /// This function is unsafe for the same reasons that `alloc` is. |
180 | /// However the allocated block of memory is guaranteed to be initialized. |
181 | /// |
182 | /// # Errors |
183 | /// |
184 | /// Returning a null pointer indicates that either memory is exhausted |
185 | /// or `layout` does not meet allocator's size or alignment constraints, |
186 | /// just as in `alloc`. |
187 | /// |
188 | /// Clients wishing to abort computation in response to an |
189 | /// allocation error are encouraged to call the [`handle_alloc_error`] function, |
190 | /// rather than directly invoking `panic!` or similar. |
191 | /// |
192 | /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html |
193 | #[stable (feature = "global_alloc" , since = "1.28.0" )] |
194 | unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { |
195 | let size = layout.size(); |
196 | // SAFETY: the safety contract for `alloc` must be upheld by the caller. |
197 | let ptr = unsafe { self.alloc(layout) }; |
198 | if !ptr.is_null() { |
199 | // SAFETY: as allocation succeeded, the region from `ptr` |
200 | // of size `size` is guaranteed to be valid for writes. |
201 | unsafe { ptr::write_bytes(ptr, 0, size) }; |
202 | } |
203 | ptr |
204 | } |
205 | |
206 | /// Shrink or grow a block of memory to the given `new_size` in bytes. |
207 | /// The block is described by the given `ptr` pointer and `layout`. |
208 | /// |
209 | /// If this returns a non-null pointer, then ownership of the memory block |
210 | /// referenced by `ptr` has been transferred to this allocator. |
211 | /// Any access to the old `ptr` is Undefined Behavior, even if the |
212 | /// allocation remained in-place. The newly returned pointer is the only valid pointer |
213 | /// for accessing this memory now. |
214 | /// |
215 | /// The new memory block is allocated with `layout`, |
216 | /// but with the `size` updated to `new_size` in bytes. |
217 | /// This new layout must be used when deallocating the new memory block with `dealloc`. |
218 | /// The range `0..min(layout.size(), new_size)` of the new memory block is |
219 | /// guaranteed to have the same values as the original block. |
220 | /// |
221 | /// If this method returns null, then ownership of the memory |
222 | /// block has not been transferred to this allocator, and the |
223 | /// contents of the memory block are unaltered. |
224 | /// |
225 | /// # Safety |
226 | /// |
227 | /// This function is unsafe because undefined behavior can result |
228 | /// if the caller does not ensure all of the following: |
229 | /// |
230 | /// * `ptr` must be currently allocated via this allocator, |
231 | /// |
232 | /// * `layout` must be the same layout that was used |
233 | /// to allocate that block of memory, |
234 | /// |
235 | /// * `new_size` must be greater than zero. |
236 | /// |
237 | /// * `new_size`, when rounded up to the nearest multiple of `layout.align()`, |
238 | /// must not overflow isize (i.e., the rounded value must be less than or |
239 | /// equal to `isize::MAX`). |
240 | /// |
241 | /// (Extension subtraits might provide more specific bounds on |
242 | /// behavior, e.g., guarantee a sentinel address or a null pointer |
243 | /// in response to a zero-size allocation request.) |
244 | /// |
245 | /// # Errors |
246 | /// |
247 | /// Returns null if the new layout does not meet the size |
248 | /// and alignment constraints of the allocator, or if reallocation |
249 | /// otherwise fails. |
250 | /// |
251 | /// Implementations are encouraged to return null on memory |
252 | /// exhaustion rather than panicking or aborting, but this is not |
253 | /// a strict requirement. (Specifically: it is *legal* to |
254 | /// implement this trait atop an underlying native allocation |
255 | /// library that aborts on memory exhaustion.) |
256 | /// |
257 | /// Clients wishing to abort computation in response to a |
258 | /// reallocation error are encouraged to call the [`handle_alloc_error`] function, |
259 | /// rather than directly invoking `panic!` or similar. |
260 | /// |
261 | /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html |
262 | #[stable (feature = "global_alloc" , since = "1.28.0" )] |
263 | unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { |
264 | // SAFETY: the caller must ensure that the `new_size` does not overflow. |
265 | // `layout.align()` comes from a `Layout` and is thus guaranteed to be valid. |
266 | let new_layout = unsafe { Layout::from_size_align_unchecked(new_size, layout.align()) }; |
267 | // SAFETY: the caller must ensure that `new_layout` is greater than zero. |
268 | let new_ptr = unsafe { self.alloc(new_layout) }; |
269 | if !new_ptr.is_null() { |
270 | // SAFETY: the previously allocated block cannot overlap the newly allocated block. |
271 | // The safety contract for `dealloc` must be upheld by the caller. |
272 | unsafe { |
273 | ptr::copy_nonoverlapping(ptr, new_ptr, cmp::min(layout.size(), new_size)); |
274 | self.dealloc(ptr, layout); |
275 | } |
276 | } |
277 | new_ptr |
278 | } |
279 | } |
280 | |