]> git.lizzy.rs Git - rust.git/blob - library/core/src/alloc/mod.rs
Update documentation to review comments
[rust.git] / library / core / src / alloc / mod.rs
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
16 /// The `AllocError` error indicates an allocation failure
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")]
21 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
22 pub struct AllocError;
23
24 // (we need this for downstream impl of trait Error)
25 #[unstable(feature = "allocator_api", issue = "32838")]
26 impl fmt::Display for AllocError {
27     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
28         f.write_str("memory allocation failed")
29     }
30 }
31
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     ///
92     /// On success, returns a [`NonNull<[u8]>`][NonNull] 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     /// Note that you may not rely on this method actually getting called, even if there are calls
98     /// to it in the source. The optimizer may detect unused allocations that it can either
99     /// eliminate entirely or move to the stack and thus never invoke the allocator here. The
100     /// optimizer may further assume that allocation is infallible, so code that used to fail due
101     /// to allocator failures may now suddenly work because the optimizer worked around the
102     /// need for an allocation.
103     /// More concretely, the following code example is unsound, irrespective of whether your
104     /// custom allocator allows counting how many allocations have happened.
105     ///
106     /// ```rust,ignore (unsound and has placeholders)
107     /// Global::dealloc(Global::alloc(some_layout));
108     /// let number_of_heap_allocs = /* call private allocator API */;
109     /// unsafe { std::intrinsics::assume(number_of_heap_allocs > 0); }
110     /// ```
111     ///
112     /// Note that the optimizations mentioned above are not the only
113     /// optimization that can be applied. You may generally not rely on heap allocations
114     /// happening if they can be removed without changing program behavior.
115     /// Whether allocations happen or not is not part of the program behavior, even if it
116     /// could be detected via an allocator that tracks allocations by printing or otherwise
117     /// having side effects.
118     ///
119     /// # Errors
120     ///
121     /// Returning `Err` indicates that either memory is exhausted or `layout` does not meet
122     /// allocator's size or alignment constraints.
123     ///
124     /// Implementations are encouraged to return `Err` on memory exhaustion rather than panicking or
125     /// aborting, but this is not a strict requirement. (Specifically: it is *legal* to implement
126     /// this trait atop an underlying native allocation library that aborts on memory exhaustion.)
127     ///
128     /// Clients wishing to abort computation in response to an allocation error are encouraged to
129     /// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar.
130     ///
131     /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
132     fn alloc(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError>;
133
134     /// Behaves like `alloc`, but also ensures that the returned memory is zero-initialized.
135     ///
136     /// # Errors
137     ///
138     /// Returning `Err` indicates that either memory is exhausted or `layout` does not meet
139     /// allocator's size or alignment constraints.
140     ///
141     /// Implementations are encouraged to return `Err` on memory exhaustion rather than panicking or
142     /// aborting, but this is not a strict requirement. (Specifically: it is *legal* to implement
143     /// this trait atop an underlying native allocation library that aborts on memory exhaustion.)
144     ///
145     /// Clients wishing to abort computation in response to an allocation error are encouraged to
146     /// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar.
147     ///
148     /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
149     fn alloc_zeroed(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
150         let ptr = self.alloc(layout)?;
151         // SAFETY: `alloc` returns a valid memory block
152         unsafe { ptr.as_non_null_ptr().as_ptr().write_bytes(0, ptr.len()) }
153         Ok(ptr)
154     }
155
156     /// Deallocates the memory referenced by `ptr`.
157     ///
158     /// # Safety
159     ///
160     /// * `ptr` must denote a block of memory [*currently allocated*] via this allocator, and
161     /// * `layout` must [*fit*] that block of memory.
162     ///
163     /// [*currently allocated*]: #currently-allocated-memory
164     /// [*fit*]: #memory-fitting
165     unsafe fn dealloc(&self, ptr: NonNull<u8>, layout: Layout);
166
167     /// Attempts to extend the memory block.
168     ///
169     /// Returns a new [`NonNull<[u8]>`][NonNull] containing a pointer and the actual size of the allocated
170     /// memory. The pointer is suitable for holding data described by `new_layout`. To accomplish
171     /// this, the allocator may extend the allocation referenced by `ptr` to fit the new layout.
172     ///
173     /// If this returns `Ok`, then ownership of the memory block referenced by `ptr` has been
174     /// transferred to this allocator. The memory may or may not have been freed, and should be
175     /// considered unusable unless it was transferred back to the caller again via the return value
176     /// of this method.
177     ///
178     /// If this method returns `Err`, then ownership of the memory block has not been transferred to
179     /// this allocator, and the contents of the memory block are unaltered.
180     ///
181     /// # Safety
182     ///
183     /// * `ptr` must denote a block of memory [*currently allocated*] via this allocator.
184     /// * `old_layout` must [*fit*] that block of memory (The `new_layout` argument need not fit it.).
185     /// * `new_layout.size()` must be greater than or equal to `old_layout.size()`.
186     ///
187     /// [*currently allocated*]: #currently-allocated-memory
188     /// [*fit*]: #memory-fitting
189     ///
190     /// # Errors
191     ///
192     /// Returns `Err` if the new layout does not meet the allocator's size and alignment
193     /// constraints of the allocator, or if growing otherwise fails.
194     ///
195     /// Implementations are encouraged to return `Err` on memory exhaustion rather than panicking or
196     /// aborting, but this is not a strict requirement. (Specifically: it is *legal* to implement
197     /// this trait atop an underlying native allocation library that aborts on memory exhaustion.)
198     ///
199     /// Clients wishing to abort computation in response to an allocation error are encouraged to
200     /// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar.
201     ///
202     /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
203     unsafe fn grow(
204         &self,
205         ptr: NonNull<u8>,
206         old_layout: Layout,
207         new_layout: Layout,
208     ) -> Result<NonNull<[u8]>, AllocError> {
209         debug_assert!(
210             new_layout.size() >= old_layout.size(),
211             "`new_layout.size()` must be greater than or equal to `old_layout.size()`"
212         );
213
214         let new_ptr = self.alloc(new_layout)?;
215
216         // SAFETY: because `new_layout.size()` must be greater than or equal to
217         // `old_layout.size()`, both the old and new memory allocation are valid for reads and
218         // writes for `old_layout.size()` bytes. Also, because the old allocation wasn't yet
219         // deallocated, it cannot overlap `new_ptr`. Thus, the call to `copy_nonoverlapping` is
220         // safe. The safety contract for `dealloc` must be upheld by the caller.
221         unsafe {
222             ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), old_layout.size());
223             self.dealloc(ptr, old_layout);
224         }
225
226         Ok(new_ptr)
227     }
228
229     /// Behaves like `grow`, but also ensures that the new contents are set to zero before being
230     /// returned.
231     ///
232     /// The memory block will contain the following contents after a successful call to
233     /// `grow_zeroed`:
234     ///   * Bytes `0..old_layout.size()` are preserved from the original allocation.
235     ///   * Bytes `old_layout.size()..old_size` will either be preserved or zeroed, depending on
236     ///     the allocator implementation. `old_size` refers to the size of the memory block prior
237     ///     to the `grow_zeroed` call, which may be larger than the size that was originally
238     ///     requested when it was allocated.
239     ///   * Bytes `old_size..new_size` are zeroed. `new_size` refers to the size of the memory
240     ///     block returned by the `grow_zeroed` call.
241     ///
242     /// # Safety
243     ///
244     /// * `ptr` must denote a block of memory [*currently allocated*] via this allocator.
245     /// * `old_layout` must [*fit*] that block of memory (The `new_layout` argument need not fit it.).
246     /// * `new_layout.size()` must be greater than or equal to `old_layout.size()`.
247     ///
248     /// [*currently allocated*]: #currently-allocated-memory
249     /// [*fit*]: #memory-fitting
250     ///
251     /// # Errors
252     ///
253     /// Returns `Err` if the new layout does not meet the allocator's size and alignment
254     /// constraints of the allocator, or if growing otherwise fails.
255     ///
256     /// Implementations are encouraged to return `Err` on memory exhaustion rather than panicking or
257     /// aborting, but this is not a strict requirement. (Specifically: it is *legal* to implement
258     /// this trait atop an underlying native allocation library that aborts on memory exhaustion.)
259     ///
260     /// Clients wishing to abort computation in response to an allocation error are encouraged to
261     /// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar.
262     ///
263     /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
264     unsafe fn grow_zeroed(
265         &self,
266         ptr: NonNull<u8>,
267         old_layout: Layout,
268         new_layout: Layout,
269     ) -> Result<NonNull<[u8]>, AllocError> {
270         debug_assert!(
271             new_layout.size() >= old_layout.size(),
272             "`new_layout.size()` must be greater than or equal to `old_layout.size()`"
273         );
274
275         let new_ptr = self.alloc_zeroed(new_layout)?;
276
277         // SAFETY: because `new_layout.size()` must be greater than or equal to
278         // `old_layout.size()`, both the old and new memory allocation are valid for reads and
279         // writes for `old_layout.size()` bytes. Also, because the old allocation wasn't yet
280         // deallocated, it cannot overlap `new_ptr`. Thus, the call to `copy_nonoverlapping` is
281         // safe. The safety contract for `dealloc` must be upheld by the caller.
282         unsafe {
283             ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), old_layout.size());
284             self.dealloc(ptr, old_layout);
285         }
286
287         Ok(new_ptr)
288     }
289
290     /// Attempts to shrink the memory block.
291     ///
292     /// Returns a new [`NonNull<[u8]>`][NonNull] containing a pointer and the actual size of the allocated
293     /// memory. The pointer is suitable for holding data described by `new_layout`. To accomplish
294     /// this, the allocator may shrink the allocation referenced by `ptr` to fit the new layout.
295     ///
296     /// If this returns `Ok`, then ownership of the memory block referenced by `ptr` has been
297     /// transferred to this allocator. The memory may or may not have been freed, and should be
298     /// considered unusable unless it was transferred back to the caller again via the return value
299     /// of this method.
300     ///
301     /// If this method returns `Err`, then ownership of the memory block has not been transferred to
302     /// this allocator, and the contents of the memory block are unaltered.
303     ///
304     /// # Safety
305     ///
306     /// * `ptr` must denote a block of memory [*currently allocated*] via this allocator.
307     /// * `old_layout` must [*fit*] that block of memory (The `new_layout` argument need not fit it.).
308     /// * `new_layout.size()` must be smaller than or equal to `old_layout.size()`.
309     ///
310     /// [*currently allocated*]: #currently-allocated-memory
311     /// [*fit*]: #memory-fitting
312     ///
313     /// # Errors
314     ///
315     /// Returns `Err` if the new layout does not meet the allocator's size and alignment
316     /// constraints of the allocator, or if shrinking otherwise fails.
317     ///
318     /// Implementations are encouraged to return `Err` on memory exhaustion rather than panicking or
319     /// aborting, but this is not a strict requirement. (Specifically: it is *legal* to implement
320     /// this trait atop an underlying native allocation library that aborts on memory exhaustion.)
321     ///
322     /// Clients wishing to abort computation in response to an allocation error are encouraged to
323     /// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar.
324     ///
325     /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
326     unsafe fn shrink(
327         &self,
328         ptr: NonNull<u8>,
329         old_layout: Layout,
330         new_layout: Layout,
331     ) -> Result<NonNull<[u8]>, AllocError> {
332         debug_assert!(
333             new_layout.size() <= old_layout.size(),
334             "`new_layout.size()` must be smaller than or equal to `old_layout.size()`"
335         );
336
337         let new_ptr = self.alloc(new_layout)?;
338
339         // SAFETY: because `new_layout.size()` must be lower than or equal to
340         // `old_layout.size()`, both the old and new memory allocation are valid for reads and
341         // writes for `new_layout.size()` bytes. Also, because the old allocation wasn't yet
342         // deallocated, it cannot overlap `new_ptr`. Thus, the call to `copy_nonoverlapping` is
343         // safe. The safety contract for `dealloc` must be upheld by the caller.
344         unsafe {
345             ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), new_layout.size());
346             self.dealloc(ptr, old_layout);
347         }
348
349         Ok(new_ptr)
350     }
351
352     /// Creates a "by reference" adaptor for this instance of `AllocRef`.
353     ///
354     /// The returned adaptor also implements `AllocRef` and will simply borrow this.
355     #[inline(always)]
356     fn by_ref(&self) -> &Self {
357         self
358     }
359 }
360
361 #[unstable(feature = "allocator_api", issue = "32838")]
362 unsafe impl<A> AllocRef for &A
363 where
364     A: AllocRef + ?Sized,
365 {
366     #[inline]
367     fn alloc(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
368         (**self).alloc(layout)
369     }
370
371     #[inline]
372     fn alloc_zeroed(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
373         (**self).alloc_zeroed(layout)
374     }
375
376     #[inline]
377     unsafe fn dealloc(&self, ptr: NonNull<u8>, layout: Layout) {
378         // SAFETY: the safety contract must be upheld by the caller
379         unsafe { (**self).dealloc(ptr, layout) }
380     }
381
382     #[inline]
383     unsafe fn grow(
384         &self,
385         ptr: NonNull<u8>,
386         old_layout: Layout,
387         new_layout: Layout,
388     ) -> Result<NonNull<[u8]>, AllocError> {
389         // SAFETY: the safety contract must be upheld by the caller
390         unsafe { (**self).grow(ptr, old_layout, new_layout) }
391     }
392
393     #[inline]
394     unsafe fn grow_zeroed(
395         &self,
396         ptr: NonNull<u8>,
397         old_layout: Layout,
398         new_layout: Layout,
399     ) -> Result<NonNull<[u8]>, AllocError> {
400         // SAFETY: the safety contract must be upheld by the caller
401         unsafe { (**self).grow_zeroed(ptr, old_layout, new_layout) }
402     }
403
404     #[inline]
405     unsafe fn shrink(
406         &self,
407         ptr: NonNull<u8>,
408         old_layout: Layout,
409         new_layout: Layout,
410     ) -> Result<NonNull<[u8]>, AllocError> {
411         // SAFETY: the safety contract must be upheld by the caller
412         unsafe { (**self).shrink(ptr, old_layout, new_layout) }
413     }
414 }