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