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