]>
Commit | Line | Data |
---|---|---|
ba9703b0 XL |
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 | /// ```no_run | |
24 | /// use std::alloc::{GlobalAlloc, Layout, alloc}; | |
25 | /// use std::ptr::null_mut; | |
26 | /// | |
27 | /// struct MyAllocator; | |
28 | /// | |
29 | /// unsafe impl GlobalAlloc for MyAllocator { | |
30 | /// unsafe fn alloc(&self, _layout: Layout) -> *mut u8 { null_mut() } | |
31 | /// unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {} | |
32 | /// } | |
33 | /// | |
34 | /// #[global_allocator] | |
35 | /// static A: MyAllocator = MyAllocator; | |
36 | /// | |
37 | /// fn main() { | |
38 | /// unsafe { | |
39 | /// assert!(alloc(Layout::new::<u32>()).is_null()) | |
40 | /// } | |
41 | /// } | |
42 | /// ``` | |
43 | /// | |
44 | /// # Safety | |
45 | /// | |
46 | /// The `GlobalAlloc` trait is an `unsafe` trait for a number of reasons, and | |
47 | /// implementors must ensure that they adhere to these contracts: | |
48 | /// | |
49 | /// * It's undefined behavior if global allocators unwind. This restriction may | |
50 | /// be lifted in the future, but currently a panic from any of these | |
51 | /// functions may lead to memory unsafety. | |
52 | /// | |
53 | /// * `Layout` queries and calculations in general must be correct. Callers of | |
54 | /// this trait are allowed to rely on the contracts defined on each method, | |
55 | /// and implementors must ensure such contracts remain true. | |
5869c6ff XL |
56 | /// |
57 | /// * You may not rely on allocations actually happening, even if there are explicit | |
58 | /// heap allocations in the source. The optimizer may detect unused allocations that it can either | |
59 | /// eliminate entirely or move to the stack and thus never invoke the allocator. The | |
60 | /// optimizer may further assume that allocation is infallible, so code that used to fail due | |
61 | /// to allocator failures may now suddenly work because the optimizer worked around the | |
62 | /// need for an allocation. More concretely, the following code example is unsound, irrespective | |
63 | /// of whether your custom allocator allows counting how many allocations have happened. | |
64 | /// | |
65 | /// ```rust,ignore (unsound and has placeholders) | |
66 | /// drop(Box::new(42)); | |
67 | /// let number_of_heap_allocs = /* call private allocator API */; | |
68 | /// unsafe { std::intrinsics::assume(number_of_heap_allocs > 0); } | |
69 | /// ``` | |
70 | /// | |
71 | /// Note that the optimizations mentioned above are not the only | |
72 | /// optimization that can be applied. You may generally not rely on heap allocations | |
73 | /// happening if they can be removed without changing program behavior. | |
74 | /// Whether allocations happen or not is not part of the program behavior, even if it | |
75 | /// could be detected via an allocator that tracks allocations by printing or otherwise | |
76 | /// having side effects. | |
ba9703b0 XL |
77 | #[stable(feature = "global_alloc", since = "1.28.0")] |
78 | pub unsafe trait GlobalAlloc { | |
79 | /// Allocate memory as described by the given `layout`. | |
80 | /// | |
81 | /// Returns a pointer to newly-allocated memory, | |
82 | /// or null to indicate allocation failure. | |
83 | /// | |
84 | /// # Safety | |
85 | /// | |
86 | /// This function is unsafe because undefined behavior can result | |
87 | /// if the caller does not ensure that `layout` has non-zero size. | |
88 | /// | |
89 | /// (Extension subtraits might provide more specific bounds on | |
90 | /// behavior, e.g., guarantee a sentinel address or a null pointer | |
91 | /// in response to a zero-size allocation request.) | |
92 | /// | |
93 | /// The allocated block of memory may or may not be initialized. | |
94 | /// | |
95 | /// # Errors | |
96 | /// | |
97 | /// Returning a null pointer indicates that either memory is exhausted | |
98 | /// or `layout` does not meet this allocator's size or alignment constraints. | |
99 | /// | |
100 | /// Implementations are encouraged to return null on memory | |
101 | /// exhaustion rather than aborting, but this is not | |
102 | /// a strict requirement. (Specifically: it is *legal* to | |
103 | /// implement this trait atop an underlying native allocation | |
104 | /// library that aborts on memory exhaustion.) | |
105 | /// | |
106 | /// Clients wishing to abort computation in response to an | |
107 | /// allocation error are encouraged to call the [`handle_alloc_error`] function, | |
108 | /// rather than directly invoking `panic!` or similar. | |
109 | /// | |
110 | /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html | |
111 | #[stable(feature = "global_alloc", since = "1.28.0")] | |
112 | unsafe fn alloc(&self, layout: Layout) -> *mut u8; | |
113 | ||
114 | /// Deallocate the block of memory at the given `ptr` pointer with the given `layout`. | |
115 | /// | |
116 | /// # Safety | |
117 | /// | |
118 | /// This function is unsafe because undefined behavior can result | |
119 | /// if the caller does not ensure all of the following: | |
120 | /// | |
121 | /// * `ptr` must denote a block of memory currently allocated via | |
122 | /// this allocator, | |
123 | /// | |
124 | /// * `layout` must be the same layout that was used | |
6a06907d | 125 | /// to allocate that block of memory. |
ba9703b0 XL |
126 | #[stable(feature = "global_alloc", since = "1.28.0")] |
127 | unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout); | |
128 | ||
129 | /// Behaves like `alloc`, but also ensures that the contents | |
130 | /// are set to zero before being returned. | |
131 | /// | |
132 | /// # Safety | |
133 | /// | |
134 | /// This function is unsafe for the same reasons that `alloc` is. | |
135 | /// However the allocated block of memory is guaranteed to be initialized. | |
136 | /// | |
137 | /// # Errors | |
138 | /// | |
139 | /// Returning a null pointer indicates that either memory is exhausted | |
140 | /// or `layout` does not meet allocator's size or alignment constraints, | |
141 | /// just as in `alloc`. | |
142 | /// | |
143 | /// Clients wishing to abort computation in response to an | |
144 | /// allocation error are encouraged to call the [`handle_alloc_error`] function, | |
145 | /// rather than directly invoking `panic!` or similar. | |
146 | /// | |
147 | /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html | |
148 | #[stable(feature = "global_alloc", since = "1.28.0")] | |
149 | unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { | |
150 | let size = layout.size(); | |
f035d41b XL |
151 | // SAFETY: the safety contract for `alloc` must be upheld by the caller. |
152 | let ptr = unsafe { self.alloc(layout) }; | |
ba9703b0 | 153 | if !ptr.is_null() { |
f035d41b XL |
154 | // SAFETY: as allocation succeeded, the region from `ptr` |
155 | // of size `size` is guaranteed to be valid for writes. | |
156 | unsafe { ptr::write_bytes(ptr, 0, size) }; | |
ba9703b0 XL |
157 | } |
158 | ptr | |
159 | } | |
160 | ||
161 | /// Shrink or grow a block of memory to the given `new_size`. | |
162 | /// The block is described by the given `ptr` pointer and `layout`. | |
163 | /// | |
164 | /// If this returns a non-null pointer, then ownership of the memory block | |
165 | /// referenced by `ptr` has been transferred to this allocator. | |
166 | /// The memory may or may not have been deallocated, | |
167 | /// and should be considered unusable (unless of course it was | |
168 | /// transferred back to the caller again via the return value of | |
169 | /// this method). The new memory block is allocated with `layout`, but | |
6a06907d XL |
170 | /// with the `size` updated to `new_size`. This new layout should be |
171 | /// used when deallocating the new memory block with `dealloc`. The range | |
172 | /// `0..min(layout.size(), new_size)` of the new memory block is | |
173 | /// guaranteed to have the same values as the original block. | |
ba9703b0 XL |
174 | /// |
175 | /// If this method returns null, then ownership of the memory | |
176 | /// block has not been transferred to this allocator, and the | |
177 | /// contents of the memory block are unaltered. | |
178 | /// | |
179 | /// # Safety | |
180 | /// | |
181 | /// This function is unsafe because undefined behavior can result | |
182 | /// if the caller does not ensure all of the following: | |
183 | /// | |
184 | /// * `ptr` must be currently allocated via this allocator, | |
185 | /// | |
186 | /// * `layout` must be the same layout that was used | |
187 | /// to allocate that block of memory, | |
188 | /// | |
189 | /// * `new_size` must be greater than zero. | |
190 | /// | |
191 | /// * `new_size`, when rounded up to the nearest multiple of `layout.align()`, | |
192 | /// must not overflow (i.e., the rounded value must be less than `usize::MAX`). | |
193 | /// | |
194 | /// (Extension subtraits might provide more specific bounds on | |
195 | /// behavior, e.g., guarantee a sentinel address or a null pointer | |
196 | /// in response to a zero-size allocation request.) | |
197 | /// | |
198 | /// # Errors | |
199 | /// | |
200 | /// Returns null if the new layout does not meet the size | |
201 | /// and alignment constraints of the allocator, or if reallocation | |
202 | /// otherwise fails. | |
203 | /// | |
204 | /// Implementations are encouraged to return null on memory | |
205 | /// exhaustion rather than panicking or aborting, but this is not | |
206 | /// a strict requirement. (Specifically: it is *legal* to | |
207 | /// implement this trait atop an underlying native allocation | |
208 | /// library that aborts on memory exhaustion.) | |
209 | /// | |
210 | /// Clients wishing to abort computation in response to a | |
211 | /// reallocation error are encouraged to call the [`handle_alloc_error`] function, | |
212 | /// rather than directly invoking `panic!` or similar. | |
213 | /// | |
214 | /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html | |
215 | #[stable(feature = "global_alloc", since = "1.28.0")] | |
216 | unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { | |
f035d41b XL |
217 | // SAFETY: the caller must ensure that the `new_size` does not overflow. |
218 | // `layout.align()` comes from a `Layout` and is thus guaranteed to be valid. | |
219 | let new_layout = unsafe { Layout::from_size_align_unchecked(new_size, layout.align()) }; | |
220 | // SAFETY: the caller must ensure that `new_layout` is greater than zero. | |
221 | let new_ptr = unsafe { self.alloc(new_layout) }; | |
ba9703b0 | 222 | if !new_ptr.is_null() { |
f035d41b XL |
223 | // SAFETY: the previously allocated block cannot overlap the newly allocated block. |
224 | // The safety contract for `dealloc` must be upheld by the caller. | |
225 | unsafe { | |
226 | ptr::copy_nonoverlapping(ptr, new_ptr, cmp::min(layout.size(), new_size)); | |
227 | self.dealloc(ptr, layout); | |
228 | } | |
ba9703b0 XL |
229 | } |
230 | new_ptr | |
231 | } | |
232 | } |