]>
Commit | Line | Data |
---|---|---|
ba9703b0 XL |
1 | //! Memory allocation APIs |
2 | ||
3 | #![stable(feature = "alloc_module", since = "1.28.0")] | |
4 | ||
5 | mod global; | |
6 | mod layout; | |
7 | ||
8 | #[stable(feature = "global_alloc", since = "1.28.0")] | |
9 | pub use self::global::GlobalAlloc; | |
10 | #[stable(feature = "alloc_layout", since = "1.28.0")] | |
11 | pub use self::layout::{Layout, LayoutErr}; | |
12 | ||
13 | use crate::fmt; | |
14 | use crate::ptr::{self, NonNull}; | |
15 | ||
1b1a35ee | 16 | /// The `AllocError` error indicates an allocation failure |
ba9703b0 XL |
17 | /// that may be due to resource exhaustion or to |
18 | /// something wrong when combining the given input arguments with this | |
19 | /// allocator. | |
20 | #[unstable(feature = "allocator_api", issue = "32838")] | |
f9f354fc | 21 | #[derive(Copy, Clone, PartialEq, Eq, Debug)] |
1b1a35ee | 22 | pub struct AllocError; |
ba9703b0 XL |
23 | |
24 | // (we need this for downstream impl of trait Error) | |
25 | #[unstable(feature = "allocator_api", issue = "32838")] | |
1b1a35ee | 26 | impl fmt::Display for AllocError { |
ba9703b0 XL |
27 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
28 | f.write_str("memory allocation failed") | |
29 | } | |
30 | } | |
31 | ||
ba9703b0 XL |
32 | /// An implementation of `AllocRef` can allocate, grow, shrink, and deallocate arbitrary blocks of |
33 | /// data described via [`Layout`][]. | |
34 | /// | |
35 | /// `AllocRef` is designed to be implemented on ZSTs, references, or smart pointers because having | |
36 | /// an allocator like `MyAlloc([u8; N])` cannot be moved, without updating the pointers to the | |
37 | /// allocated memory. | |
38 | /// | |
39 | /// Unlike [`GlobalAlloc`][], zero-sized allocations are allowed in `AllocRef`. If an underlying | |
40 | /// allocator does not support this (like jemalloc) or return a null pointer (such as | |
41 | /// `libc::malloc`), this must be caught by the implementation. | |
42 | /// | |
43 | /// ### Currently allocated memory | |
44 | /// | |
45 | /// Some of the methods require that a memory block be *currently allocated* via an allocator. This | |
46 | /// means that: | |
47 | /// | |
48 | /// * the starting address for that memory block was previously returned by [`alloc`], [`grow`], or | |
49 | /// [`shrink`], and | |
50 | /// | |
51 | /// * the memory block has not been subsequently deallocated, where blocks are either deallocated | |
52 | /// directly by being passed to [`dealloc`] or were changed by being passed to [`grow`] or | |
53 | /// [`shrink`] that returns `Ok`. If `grow` or `shrink` have returned `Err`, the passed pointer | |
54 | /// remains valid. | |
55 | /// | |
56 | /// [`alloc`]: AllocRef::alloc | |
57 | /// [`grow`]: AllocRef::grow | |
58 | /// [`shrink`]: AllocRef::shrink | |
59 | /// [`dealloc`]: AllocRef::dealloc | |
60 | /// | |
61 | /// ### Memory fitting | |
62 | /// | |
63 | /// Some of the methods require that a layout *fit* a memory block. What it means for a layout to | |
64 | /// "fit" a memory block means (or equivalently, for a memory block to "fit" a layout) is that the | |
65 | /// following conditions must hold: | |
66 | /// | |
67 | /// * The block must be allocated with the same alignment as [`layout.align()`], and | |
68 | /// | |
69 | /// * The provided [`layout.size()`] must fall in the range `min ..= max`, where: | |
70 | /// - `min` is the size of the layout most recently used to allocate the block, and | |
71 | /// - `max` is the latest actual size returned from [`alloc`], [`grow`], or [`shrink`]. | |
72 | /// | |
73 | /// [`layout.align()`]: Layout::align | |
74 | /// [`layout.size()`]: Layout::size | |
75 | /// | |
76 | /// # Safety | |
77 | /// | |
78 | /// * Memory blocks returned from an allocator must point to valid memory and retain their validity | |
79 | /// until the instance and all of its clones are dropped, | |
80 | /// | |
81 | /// * cloning or moving the allocator must not invalidate memory blocks returned from this | |
82 | /// allocator. A cloned allocator must behave like the same allocator, and | |
83 | /// | |
84 | /// * any pointer to a memory block which is [*currently allocated*] may be passed to any other | |
85 | /// method of the allocator. | |
86 | /// | |
87 | /// [*currently allocated*]: #currently-allocated-memory | |
88 | #[unstable(feature = "allocator_api", issue = "32838")] | |
89 | pub unsafe trait AllocRef { | |
90 | /// Attempts to allocate a block of memory. | |
91 | /// | |
3dfed10e XL |
92 | /// On success, returns a [`NonNull<[u8]>`] meeting the size and alignment guarantees of `layout`. |
93 | /// | |
94 | /// The returned block may have a larger size than specified by `layout.size()`, and may or may | |
95 | /// not have its contents initialized. | |
96 | /// | |
97 | /// [`NonNull<[u8]>`]: NonNull | |
98 | /// | |
99 | /// # Errors | |
100 | /// | |
101 | /// Returning `Err` indicates that either memory is exhausted or `layout` does not meet | |
102 | /// allocator's size or alignment constraints. | |
103 | /// | |
104 | /// Implementations are encouraged to return `Err` on memory exhaustion rather than panicking or | |
105 | /// aborting, but this is not a strict requirement. (Specifically: it is *legal* to implement | |
106 | /// this trait atop an underlying native allocation library that aborts on memory exhaustion.) | |
ba9703b0 | 107 | /// |
3dfed10e XL |
108 | /// Clients wishing to abort computation in response to an allocation error are encouraged to |
109 | /// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar. | |
ba9703b0 | 110 | /// |
3dfed10e | 111 | /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html |
1b1a35ee | 112 | fn alloc(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError>; |
3dfed10e XL |
113 | |
114 | /// Behaves like `alloc`, but also ensures that the returned memory is zero-initialized. | |
ba9703b0 XL |
115 | /// |
116 | /// # Errors | |
117 | /// | |
118 | /// Returning `Err` indicates that either memory is exhausted or `layout` does not meet | |
119 | /// allocator's size or alignment constraints. | |
120 | /// | |
121 | /// Implementations are encouraged to return `Err` on memory exhaustion rather than panicking or | |
122 | /// aborting, but this is not a strict requirement. (Specifically: it is *legal* to implement | |
123 | /// this trait atop an underlying native allocation library that aborts on memory exhaustion.) | |
124 | /// | |
125 | /// Clients wishing to abort computation in response to an allocation error are encouraged to | |
126 | /// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar. | |
127 | /// | |
128 | /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html | |
1b1a35ee | 129 | fn alloc_zeroed(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> { |
3dfed10e XL |
130 | let ptr = self.alloc(layout)?; |
131 | // SAFETY: `alloc` returns a valid memory block | |
132 | unsafe { ptr.as_non_null_ptr().as_ptr().write_bytes(0, ptr.len()) } | |
133 | Ok(ptr) | |
134 | } | |
ba9703b0 XL |
135 | |
136 | /// Deallocates the memory referenced by `ptr`. | |
137 | /// | |
138 | /// # Safety | |
139 | /// | |
140 | /// * `ptr` must denote a block of memory [*currently allocated*] via this allocator, and | |
141 | /// * `layout` must [*fit*] that block of memory. | |
142 | /// | |
143 | /// [*currently allocated*]: #currently-allocated-memory | |
144 | /// [*fit*]: #memory-fitting | |
1b1a35ee | 145 | unsafe fn dealloc(&self, ptr: NonNull<u8>, layout: Layout); |
ba9703b0 XL |
146 | |
147 | /// Attempts to extend the memory block. | |
148 | /// | |
3dfed10e | 149 | /// Returns a new [`NonNull<[u8]>`] containing a pointer and the actual size of the allocated |
1b1a35ee XL |
150 | /// memory. The pointer is suitable for holding data described by `new_layout`. To accomplish |
151 | /// this, the allocator may extend the allocation referenced by `ptr` to fit the new layout. | |
ba9703b0 | 152 | /// |
3dfed10e XL |
153 | /// If this returns `Ok`, then ownership of the memory block referenced by `ptr` has been |
154 | /// transferred to this allocator. The memory may or may not have been freed, and should be | |
155 | /// considered unusable unless it was transferred back to the caller again via the return value | |
156 | /// of this method. | |
ba9703b0 XL |
157 | /// |
158 | /// If this method returns `Err`, then ownership of the memory block has not been transferred to | |
159 | /// this allocator, and the contents of the memory block are unaltered. | |
160 | /// | |
3dfed10e | 161 | /// [`NonNull<[u8]>`]: NonNull |
ba9703b0 XL |
162 | /// |
163 | /// # Safety | |
164 | /// | |
1b1a35ee XL |
165 | /// * `ptr` must denote a block of memory [*currently allocated*] via this allocator. |
166 | /// * `old_layout` must [*fit*] that block of memory (The `new_layout` argument need not fit it.). | |
167 | /// * `new_layout.size()` must be greater than or equal to `old_layout.size()`. | |
ba9703b0 XL |
168 | /// |
169 | /// [*currently allocated*]: #currently-allocated-memory | |
170 | /// [*fit*]: #memory-fitting | |
171 | /// | |
172 | /// # Errors | |
173 | /// | |
174 | /// Returns `Err` if the new layout does not meet the allocator's size and alignment | |
175 | /// constraints of the allocator, or if growing otherwise fails. | |
176 | /// | |
177 | /// Implementations are encouraged to return `Err` on memory exhaustion rather than panicking or | |
178 | /// aborting, but this is not a strict requirement. (Specifically: it is *legal* to implement | |
179 | /// this trait atop an underlying native allocation library that aborts on memory exhaustion.) | |
180 | /// | |
181 | /// Clients wishing to abort computation in response to an allocation error are encouraged to | |
182 | /// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar. | |
183 | /// | |
184 | /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html | |
185 | unsafe fn grow( | |
1b1a35ee | 186 | &self, |
ba9703b0 | 187 | ptr: NonNull<u8>, |
1b1a35ee XL |
188 | old_layout: Layout, |
189 | new_layout: Layout, | |
190 | ) -> Result<NonNull<[u8]>, AllocError> { | |
3dfed10e | 191 | debug_assert!( |
1b1a35ee XL |
192 | new_layout.size() >= old_layout.size(), |
193 | "`new_layout.size()` must be greater than or equal to `old_layout.size()`" | |
3dfed10e XL |
194 | ); |
195 | ||
3dfed10e XL |
196 | let new_ptr = self.alloc(new_layout)?; |
197 | ||
1b1a35ee XL |
198 | // SAFETY: because `new_layout.size()` must be greater than or equal to |
199 | // `old_layout.size()`, both the old and new memory allocation are valid for reads and | |
200 | // writes for `old_layout.size()` bytes. Also, because the old allocation wasn't yet | |
201 | // deallocated, it cannot overlap `new_ptr`. Thus, the call to `copy_nonoverlapping` is | |
202 | // safe. The safety contract for `dealloc` must be upheld by the caller. | |
3dfed10e | 203 | unsafe { |
1b1a35ee XL |
204 | ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), old_layout.size()); |
205 | self.dealloc(ptr, old_layout); | |
3dfed10e | 206 | } |
ba9703b0 | 207 | |
3dfed10e XL |
208 | Ok(new_ptr) |
209 | } | |
ba9703b0 | 210 | |
3dfed10e XL |
211 | /// Behaves like `grow`, but also ensures that the new contents are set to zero before being |
212 | /// returned. | |
213 | /// | |
214 | /// The memory block will contain the following contents after a successful call to | |
215 | /// `grow_zeroed`: | |
1b1a35ee XL |
216 | /// * Bytes `0..old_layout.size()` are preserved from the original allocation. |
217 | /// * Bytes `old_layout.size()..old_size` will either be preserved or zeroed, depending on | |
218 | /// the allocator implementation. `old_size` refers to the size of the memory block prior | |
219 | /// to the `grow_zeroed` call, which may be larger than the size that was originally | |
220 | /// requested when it was allocated. | |
3dfed10e | 221 | /// * Bytes `old_size..new_size` are zeroed. `new_size` refers to the size of the memory |
1b1a35ee | 222 | /// block returned by the `grow_zeroed` call. |
3dfed10e XL |
223 | /// |
224 | /// # Safety | |
225 | /// | |
1b1a35ee XL |
226 | /// * `ptr` must denote a block of memory [*currently allocated*] via this allocator. |
227 | /// * `old_layout` must [*fit*] that block of memory (The `new_layout` argument need not fit it.). | |
228 | /// * `new_layout.size()` must be greater than or equal to `old_layout.size()`. | |
3dfed10e XL |
229 | /// |
230 | /// [*currently allocated*]: #currently-allocated-memory | |
231 | /// [*fit*]: #memory-fitting | |
232 | /// | |
233 | /// # Errors | |
234 | /// | |
235 | /// Returns `Err` if the new layout does not meet the allocator's size and alignment | |
236 | /// constraints of the allocator, or if growing otherwise fails. | |
237 | /// | |
238 | /// Implementations are encouraged to return `Err` on memory exhaustion rather than panicking or | |
239 | /// aborting, but this is not a strict requirement. (Specifically: it is *legal* to implement | |
240 | /// this trait atop an underlying native allocation library that aborts on memory exhaustion.) | |
241 | /// | |
242 | /// Clients wishing to abort computation in response to an allocation error are encouraged to | |
243 | /// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar. | |
244 | /// | |
245 | /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html | |
246 | unsafe fn grow_zeroed( | |
1b1a35ee | 247 | &self, |
3dfed10e | 248 | ptr: NonNull<u8>, |
1b1a35ee XL |
249 | old_layout: Layout, |
250 | new_layout: Layout, | |
251 | ) -> Result<NonNull<[u8]>, AllocError> { | |
3dfed10e | 252 | debug_assert!( |
1b1a35ee XL |
253 | new_layout.size() >= old_layout.size(), |
254 | "`new_layout.size()` must be greater than or equal to `old_layout.size()`" | |
3dfed10e XL |
255 | ); |
256 | ||
3dfed10e | 257 | let new_ptr = self.alloc_zeroed(new_layout)?; |
f035d41b | 258 | |
1b1a35ee XL |
259 | // SAFETY: because `new_layout.size()` must be greater than or equal to |
260 | // `old_layout.size()`, both the old and new memory allocation are valid for reads and | |
261 | // writes for `old_layout.size()` bytes. Also, because the old allocation wasn't yet | |
262 | // deallocated, it cannot overlap `new_ptr`. Thus, the call to `copy_nonoverlapping` is | |
263 | // safe. The safety contract for `dealloc` must be upheld by the caller. | |
3dfed10e | 264 | unsafe { |
1b1a35ee XL |
265 | ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), old_layout.size()); |
266 | self.dealloc(ptr, old_layout); | |
ba9703b0 | 267 | } |
3dfed10e XL |
268 | |
269 | Ok(new_ptr) | |
ba9703b0 XL |
270 | } |
271 | ||
272 | /// Attempts to shrink the memory block. | |
273 | /// | |
3dfed10e | 274 | /// Returns a new [`NonNull<[u8]>`] containing a pointer and the actual size of the allocated |
1b1a35ee XL |
275 | /// memory. The pointer is suitable for holding data described by `new_layout`. To accomplish |
276 | /// this, the allocator may shrink the allocation referenced by `ptr` to fit the new layout. | |
ba9703b0 XL |
277 | /// |
278 | /// If this returns `Ok`, then ownership of the memory block referenced by `ptr` has been | |
279 | /// transferred to this allocator. The memory may or may not have been freed, and should be | |
3dfed10e XL |
280 | /// considered unusable unless it was transferred back to the caller again via the return value |
281 | /// of this method. | |
ba9703b0 XL |
282 | /// |
283 | /// If this method returns `Err`, then ownership of the memory block has not been transferred to | |
284 | /// this allocator, and the contents of the memory block are unaltered. | |
285 | /// | |
3dfed10e | 286 | /// [`NonNull<[u8]>`]: NonNull |
ba9703b0 XL |
287 | /// |
288 | /// # Safety | |
289 | /// | |
1b1a35ee XL |
290 | /// * `ptr` must denote a block of memory [*currently allocated*] via this allocator. |
291 | /// * `old_layout` must [*fit*] that block of memory (The `new_layout` argument need not fit it.). | |
292 | /// * `new_layout.size()` must be smaller than or equal to `old_layout.size()`. | |
ba9703b0 XL |
293 | /// |
294 | /// [*currently allocated*]: #currently-allocated-memory | |
295 | /// [*fit*]: #memory-fitting | |
296 | /// | |
297 | /// # Errors | |
298 | /// | |
299 | /// Returns `Err` if the new layout does not meet the allocator's size and alignment | |
300 | /// constraints of the allocator, or if shrinking otherwise fails. | |
301 | /// | |
302 | /// Implementations are encouraged to return `Err` on memory exhaustion rather than panicking or | |
303 | /// aborting, but this is not a strict requirement. (Specifically: it is *legal* to implement | |
304 | /// this trait atop an underlying native allocation library that aborts on memory exhaustion.) | |
305 | /// | |
306 | /// Clients wishing to abort computation in response to an allocation error are encouraged to | |
307 | /// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar. | |
308 | /// | |
309 | /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html | |
310 | unsafe fn shrink( | |
1b1a35ee | 311 | &self, |
ba9703b0 | 312 | ptr: NonNull<u8>, |
1b1a35ee XL |
313 | old_layout: Layout, |
314 | new_layout: Layout, | |
315 | ) -> Result<NonNull<[u8]>, AllocError> { | |
3dfed10e | 316 | debug_assert!( |
1b1a35ee XL |
317 | new_layout.size() <= old_layout.size(), |
318 | "`new_layout.size()` must be smaller than or equal to `old_layout.size()`" | |
3dfed10e | 319 | ); |
ba9703b0 | 320 | |
3dfed10e | 321 | let new_ptr = self.alloc(new_layout)?; |
f035d41b | 322 | |
1b1a35ee XL |
323 | // SAFETY: because `new_layout.size()` must be lower than or equal to |
324 | // `old_layout.size()`, both the old and new memory allocation are valid for reads and | |
325 | // writes for `new_layout.size()` bytes. Also, because the old allocation wasn't yet | |
326 | // deallocated, it cannot overlap `new_ptr`. Thus, the call to `copy_nonoverlapping` is | |
327 | // safe. The safety contract for `dealloc` must be upheld by the caller. | |
3dfed10e | 328 | unsafe { |
1b1a35ee XL |
329 | ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), new_layout.size()); |
330 | self.dealloc(ptr, old_layout); | |
ba9703b0 | 331 | } |
3dfed10e XL |
332 | |
333 | Ok(new_ptr) | |
ba9703b0 | 334 | } |
f9f354fc XL |
335 | |
336 | /// Creates a "by reference" adaptor for this instance of `AllocRef`. | |
337 | /// | |
338 | /// The returned adaptor also implements `AllocRef` and will simply borrow this. | |
339 | #[inline(always)] | |
1b1a35ee | 340 | fn by_ref(&self) -> &Self { |
f9f354fc XL |
341 | self |
342 | } | |
343 | } | |
344 | ||
345 | #[unstable(feature = "allocator_api", issue = "32838")] | |
1b1a35ee | 346 | unsafe impl<A> AllocRef for &A |
f9f354fc XL |
347 | where |
348 | A: AllocRef + ?Sized, | |
349 | { | |
350 | #[inline] | |
1b1a35ee | 351 | fn alloc(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> { |
3dfed10e XL |
352 | (**self).alloc(layout) |
353 | } | |
354 | ||
355 | #[inline] | |
1b1a35ee | 356 | fn alloc_zeroed(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> { |
3dfed10e | 357 | (**self).alloc_zeroed(layout) |
f9f354fc XL |
358 | } |
359 | ||
360 | #[inline] | |
1b1a35ee | 361 | unsafe fn dealloc(&self, ptr: NonNull<u8>, layout: Layout) { |
f035d41b XL |
362 | // SAFETY: the safety contract must be upheld by the caller |
363 | unsafe { (**self).dealloc(ptr, layout) } | |
f9f354fc XL |
364 | } |
365 | ||
366 | #[inline] | |
367 | unsafe fn grow( | |
1b1a35ee | 368 | &self, |
f9f354fc | 369 | ptr: NonNull<u8>, |
1b1a35ee XL |
370 | old_layout: Layout, |
371 | new_layout: Layout, | |
372 | ) -> Result<NonNull<[u8]>, AllocError> { | |
3dfed10e | 373 | // SAFETY: the safety contract must be upheld by the caller |
1b1a35ee | 374 | unsafe { (**self).grow(ptr, old_layout, new_layout) } |
3dfed10e XL |
375 | } |
376 | ||
377 | #[inline] | |
378 | unsafe fn grow_zeroed( | |
1b1a35ee | 379 | &self, |
3dfed10e | 380 | ptr: NonNull<u8>, |
1b1a35ee XL |
381 | old_layout: Layout, |
382 | new_layout: Layout, | |
383 | ) -> Result<NonNull<[u8]>, AllocError> { | |
f035d41b | 384 | // SAFETY: the safety contract must be upheld by the caller |
1b1a35ee | 385 | unsafe { (**self).grow_zeroed(ptr, old_layout, new_layout) } |
f9f354fc XL |
386 | } |
387 | ||
388 | #[inline] | |
389 | unsafe fn shrink( | |
1b1a35ee | 390 | &self, |
f9f354fc | 391 | ptr: NonNull<u8>, |
1b1a35ee XL |
392 | old_layout: Layout, |
393 | new_layout: Layout, | |
394 | ) -> Result<NonNull<[u8]>, AllocError> { | |
f035d41b | 395 | // SAFETY: the safety contract must be upheld by the caller |
1b1a35ee | 396 | unsafe { (**self).shrink(ptr, old_layout, new_layout) } |
f9f354fc | 397 | } |
ba9703b0 | 398 | } |