]>
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")] | |
13 | #[rustc_deprecated( | |
14 | since = "1.52.0", | |
15 | reason = "Name does not follow std convention, use LayoutError", | |
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 | |
164 | /// considered unusable unless it was transferred back to the caller again via the return value | |
165 | /// of this method. | |
ba9703b0 XL |
166 | /// |
167 | /// If this method returns `Err`, then ownership of the memory block has not been transferred to | |
168 | /// this allocator, and the contents of the memory block are unaltered. | |
169 | /// | |
ba9703b0 XL |
170 | /// # Safety |
171 | /// | |
1b1a35ee XL |
172 | /// * `ptr` must denote a block of memory [*currently allocated*] via this allocator. |
173 | /// * `old_layout` must [*fit*] that block of memory (The `new_layout` argument need not fit it.). | |
174 | /// * `new_layout.size()` must be greater than or equal to `old_layout.size()`. | |
ba9703b0 XL |
175 | /// |
176 | /// [*currently allocated*]: #currently-allocated-memory | |
177 | /// [*fit*]: #memory-fitting | |
178 | /// | |
179 | /// # Errors | |
180 | /// | |
181 | /// Returns `Err` if the new layout does not meet the allocator's size and alignment | |
182 | /// constraints of the allocator, or if growing otherwise fails. | |
183 | /// | |
184 | /// Implementations are encouraged to return `Err` on memory exhaustion rather than panicking or | |
185 | /// aborting, but this is not a strict requirement. (Specifically: it is *legal* to implement | |
186 | /// this trait atop an underlying native allocation library that aborts on memory exhaustion.) | |
187 | /// | |
188 | /// Clients wishing to abort computation in response to an allocation error are encouraged to | |
189 | /// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar. | |
190 | /// | |
191 | /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html | |
192 | unsafe fn grow( | |
1b1a35ee | 193 | &self, |
ba9703b0 | 194 | ptr: NonNull<u8>, |
1b1a35ee XL |
195 | old_layout: Layout, |
196 | new_layout: Layout, | |
197 | ) -> Result<NonNull<[u8]>, AllocError> { | |
3dfed10e | 198 | debug_assert!( |
1b1a35ee XL |
199 | new_layout.size() >= old_layout.size(), |
200 | "`new_layout.size()` must be greater than or equal to `old_layout.size()`" | |
3dfed10e XL |
201 | ); |
202 | ||
fc512014 | 203 | let new_ptr = self.allocate(new_layout)?; |
3dfed10e | 204 | |
1b1a35ee XL |
205 | // SAFETY: because `new_layout.size()` must be greater than or equal to |
206 | // `old_layout.size()`, both the old and new memory allocation are valid for reads and | |
207 | // writes for `old_layout.size()` bytes. Also, because the old allocation wasn't yet | |
208 | // deallocated, it cannot overlap `new_ptr`. Thus, the call to `copy_nonoverlapping` is | |
209 | // safe. The safety contract for `dealloc` must be upheld by the caller. | |
3dfed10e | 210 | unsafe { |
1b1a35ee | 211 | ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), old_layout.size()); |
fc512014 | 212 | self.deallocate(ptr, old_layout); |
3dfed10e | 213 | } |
ba9703b0 | 214 | |
3dfed10e XL |
215 | Ok(new_ptr) |
216 | } | |
ba9703b0 | 217 | |
3dfed10e XL |
218 | /// Behaves like `grow`, but also ensures that the new contents are set to zero before being |
219 | /// returned. | |
220 | /// | |
221 | /// The memory block will contain the following contents after a successful call to | |
222 | /// `grow_zeroed`: | |
1b1a35ee XL |
223 | /// * Bytes `0..old_layout.size()` are preserved from the original allocation. |
224 | /// * Bytes `old_layout.size()..old_size` will either be preserved or zeroed, depending on | |
225 | /// the allocator implementation. `old_size` refers to the size of the memory block prior | |
226 | /// to the `grow_zeroed` call, which may be larger than the size that was originally | |
227 | /// requested when it was allocated. | |
3dfed10e | 228 | /// * Bytes `old_size..new_size` are zeroed. `new_size` refers to the size of the memory |
1b1a35ee | 229 | /// block returned by the `grow_zeroed` call. |
3dfed10e XL |
230 | /// |
231 | /// # Safety | |
232 | /// | |
1b1a35ee XL |
233 | /// * `ptr` must denote a block of memory [*currently allocated*] via this allocator. |
234 | /// * `old_layout` must [*fit*] that block of memory (The `new_layout` argument need not fit it.). | |
235 | /// * `new_layout.size()` must be greater than or equal to `old_layout.size()`. | |
3dfed10e XL |
236 | /// |
237 | /// [*currently allocated*]: #currently-allocated-memory | |
238 | /// [*fit*]: #memory-fitting | |
239 | /// | |
240 | /// # Errors | |
241 | /// | |
242 | /// Returns `Err` if the new layout does not meet the allocator's size and alignment | |
243 | /// constraints of the allocator, or if growing otherwise fails. | |
244 | /// | |
245 | /// Implementations are encouraged to return `Err` on memory exhaustion rather than panicking or | |
246 | /// aborting, but this is not a strict requirement. (Specifically: it is *legal* to implement | |
247 | /// this trait atop an underlying native allocation library that aborts on memory exhaustion.) | |
248 | /// | |
249 | /// Clients wishing to abort computation in response to an allocation error are encouraged to | |
250 | /// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar. | |
251 | /// | |
252 | /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html | |
253 | unsafe fn grow_zeroed( | |
1b1a35ee | 254 | &self, |
3dfed10e | 255 | ptr: NonNull<u8>, |
1b1a35ee XL |
256 | old_layout: Layout, |
257 | new_layout: Layout, | |
258 | ) -> Result<NonNull<[u8]>, AllocError> { | |
3dfed10e | 259 | debug_assert!( |
1b1a35ee XL |
260 | new_layout.size() >= old_layout.size(), |
261 | "`new_layout.size()` must be greater than or equal to `old_layout.size()`" | |
3dfed10e XL |
262 | ); |
263 | ||
fc512014 | 264 | let new_ptr = self.allocate_zeroed(new_layout)?; |
f035d41b | 265 | |
1b1a35ee XL |
266 | // SAFETY: because `new_layout.size()` must be greater than or equal to |
267 | // `old_layout.size()`, both the old and new memory allocation are valid for reads and | |
268 | // writes for `old_layout.size()` bytes. Also, because the old allocation wasn't yet | |
269 | // deallocated, it cannot overlap `new_ptr`. Thus, the call to `copy_nonoverlapping` is | |
270 | // safe. The safety contract for `dealloc` must be upheld by the caller. | |
3dfed10e | 271 | unsafe { |
1b1a35ee | 272 | ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), old_layout.size()); |
fc512014 | 273 | self.deallocate(ptr, old_layout); |
ba9703b0 | 274 | } |
3dfed10e XL |
275 | |
276 | Ok(new_ptr) | |
ba9703b0 XL |
277 | } |
278 | ||
279 | /// Attempts to shrink the memory block. | |
280 | /// | |
29967ef6 | 281 | /// Returns a new [`NonNull<[u8]>`][NonNull] containing a pointer and the actual size of the allocated |
1b1a35ee XL |
282 | /// memory. The pointer is suitable for holding data described by `new_layout`. To accomplish |
283 | /// this, the allocator may shrink the allocation referenced by `ptr` to fit the new layout. | |
ba9703b0 XL |
284 | /// |
285 | /// If this returns `Ok`, then ownership of the memory block referenced by `ptr` has been | |
286 | /// transferred to this allocator. The memory may or may not have been freed, and should be | |
3dfed10e XL |
287 | /// considered unusable unless it was transferred back to the caller again via the return value |
288 | /// of this method. | |
ba9703b0 XL |
289 | /// |
290 | /// If this method returns `Err`, then ownership of the memory block has not been transferred to | |
291 | /// this allocator, and the contents of the memory block are unaltered. | |
292 | /// | |
ba9703b0 XL |
293 | /// # Safety |
294 | /// | |
1b1a35ee XL |
295 | /// * `ptr` must denote a block of memory [*currently allocated*] via this allocator. |
296 | /// * `old_layout` must [*fit*] that block of memory (The `new_layout` argument need not fit it.). | |
297 | /// * `new_layout.size()` must be smaller than or equal to `old_layout.size()`. | |
ba9703b0 XL |
298 | /// |
299 | /// [*currently allocated*]: #currently-allocated-memory | |
300 | /// [*fit*]: #memory-fitting | |
301 | /// | |
302 | /// # Errors | |
303 | /// | |
304 | /// Returns `Err` if the new layout does not meet the allocator's size and alignment | |
305 | /// constraints of the allocator, or if shrinking otherwise fails. | |
306 | /// | |
307 | /// Implementations are encouraged to return `Err` on memory exhaustion rather than panicking or | |
308 | /// aborting, but this is not a strict requirement. (Specifically: it is *legal* to implement | |
309 | /// this trait atop an underlying native allocation library that aborts on memory exhaustion.) | |
310 | /// | |
311 | /// Clients wishing to abort computation in response to an allocation error are encouraged to | |
312 | /// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar. | |
313 | /// | |
314 | /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html | |
315 | unsafe fn shrink( | |
1b1a35ee | 316 | &self, |
ba9703b0 | 317 | ptr: NonNull<u8>, |
1b1a35ee XL |
318 | old_layout: Layout, |
319 | new_layout: Layout, | |
320 | ) -> Result<NonNull<[u8]>, AllocError> { | |
3dfed10e | 321 | debug_assert!( |
1b1a35ee XL |
322 | new_layout.size() <= old_layout.size(), |
323 | "`new_layout.size()` must be smaller than or equal to `old_layout.size()`" | |
3dfed10e | 324 | ); |
ba9703b0 | 325 | |
fc512014 | 326 | let new_ptr = self.allocate(new_layout)?; |
f035d41b | 327 | |
1b1a35ee XL |
328 | // SAFETY: because `new_layout.size()` must be lower than or equal to |
329 | // `old_layout.size()`, both the old and new memory allocation are valid for reads and | |
330 | // writes for `new_layout.size()` bytes. Also, because the old allocation wasn't yet | |
331 | // deallocated, it cannot overlap `new_ptr`. Thus, the call to `copy_nonoverlapping` is | |
332 | // safe. The safety contract for `dealloc` must be upheld by the caller. | |
3dfed10e | 333 | unsafe { |
1b1a35ee | 334 | ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), new_layout.size()); |
fc512014 | 335 | self.deallocate(ptr, old_layout); |
ba9703b0 | 336 | } |
3dfed10e XL |
337 | |
338 | Ok(new_ptr) | |
ba9703b0 | 339 | } |
f9f354fc | 340 | |
94222f64 | 341 | /// Creates a "by reference" adapter for this instance of `Allocator`. |
f9f354fc | 342 | /// |
94222f64 | 343 | /// The returned adapter also implements `Allocator` and will simply borrow this. |
f9f354fc | 344 | #[inline(always)] |
5869c6ff XL |
345 | fn by_ref(&self) -> &Self |
346 | where | |
347 | Self: Sized, | |
348 | { | |
f9f354fc XL |
349 | self |
350 | } | |
351 | } | |
352 | ||
353 | #[unstable(feature = "allocator_api", issue = "32838")] | |
fc512014 | 354 | unsafe impl<A> Allocator for &A |
f9f354fc | 355 | where |
fc512014 | 356 | A: Allocator + ?Sized, |
f9f354fc XL |
357 | { |
358 | #[inline] | |
fc512014 XL |
359 | fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> { |
360 | (**self).allocate(layout) | |
3dfed10e XL |
361 | } |
362 | ||
363 | #[inline] | |
fc512014 XL |
364 | fn allocate_zeroed(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> { |
365 | (**self).allocate_zeroed(layout) | |
f9f354fc XL |
366 | } |
367 | ||
368 | #[inline] | |
fc512014 | 369 | unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) { |
f035d41b | 370 | // SAFETY: the safety contract must be upheld by the caller |
fc512014 | 371 | unsafe { (**self).deallocate(ptr, layout) } |
f9f354fc XL |
372 | } |
373 | ||
374 | #[inline] | |
375 | unsafe fn grow( | |
1b1a35ee | 376 | &self, |
f9f354fc | 377 | ptr: NonNull<u8>, |
1b1a35ee XL |
378 | old_layout: Layout, |
379 | new_layout: Layout, | |
380 | ) -> Result<NonNull<[u8]>, AllocError> { | |
3dfed10e | 381 | // SAFETY: the safety contract must be upheld by the caller |
1b1a35ee | 382 | unsafe { (**self).grow(ptr, old_layout, new_layout) } |
3dfed10e XL |
383 | } |
384 | ||
385 | #[inline] | |
386 | unsafe fn grow_zeroed( | |
1b1a35ee | 387 | &self, |
3dfed10e | 388 | ptr: NonNull<u8>, |
1b1a35ee XL |
389 | old_layout: Layout, |
390 | new_layout: Layout, | |
391 | ) -> Result<NonNull<[u8]>, AllocError> { | |
f035d41b | 392 | // SAFETY: the safety contract must be upheld by the caller |
1b1a35ee | 393 | unsafe { (**self).grow_zeroed(ptr, old_layout, new_layout) } |
f9f354fc XL |
394 | } |
395 | ||
396 | #[inline] | |
397 | unsafe fn shrink( | |
1b1a35ee | 398 | &self, |
f9f354fc | 399 | ptr: NonNull<u8>, |
1b1a35ee XL |
400 | old_layout: Layout, |
401 | new_layout: Layout, | |
402 | ) -> Result<NonNull<[u8]>, AllocError> { | |
f035d41b | 403 | // SAFETY: the safety contract must be upheld by the caller |
1b1a35ee | 404 | unsafe { (**self).shrink(ptr, old_layout, new_layout) } |
f9f354fc | 405 | } |
ba9703b0 | 406 | } |