]> git.lizzy.rs Git - rust.git/blob - library/core/src/alloc/mod.rs
Rollup merge of #75780 - matklad:unconfuseunpindocs, r=KodrAus
[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 `AllocErr` 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 AllocErr;
23
24 // (we need this for downstream impl of trait Error)
25 #[unstable(feature = "allocator_api", issue = "32838")]
26 impl fmt::Display for AllocErr {
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]>`] 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.)
107     ///
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.
110     ///
111     /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
112     fn alloc(&mut self, layout: Layout) -> Result<NonNull<[u8]>, AllocErr>;
113
114     /// Behaves like `alloc`, but also ensures that the returned memory is zero-initialized.
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
129     fn alloc_zeroed(&mut self, layout: Layout) -> Result<NonNull<[u8]>, AllocErr> {
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     }
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
145     unsafe fn dealloc(&mut self, ptr: NonNull<u8>, layout: Layout);
146
147     /// Attempts to extend the memory block.
148     ///
149     /// Returns a new [`NonNull<[u8]>`] containing a pointer and the actual size of the allocated
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.
152     ///
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.
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     ///
161     /// [`NonNull<[u8]>`]: NonNull
162     ///
163     /// # Safety
164     ///
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()`.
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(
186         &mut self,
187         ptr: NonNull<u8>,
188         old_layout: Layout,
189         new_layout: Layout,
190     ) -> Result<NonNull<[u8]>, AllocErr> {
191         debug_assert!(
192             new_layout.size() >= old_layout.size(),
193             "`new_layout.size()` must be greater than or equal to `old_layout.size()`"
194         );
195
196         let new_ptr = self.alloc(new_layout)?;
197
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.
203         unsafe {
204             ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), old_layout.size());
205             self.dealloc(ptr, old_layout);
206         }
207
208         Ok(new_ptr)
209     }
210
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`:
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.
221     ///   * Bytes `old_size..new_size` are zeroed. `new_size` refers to the size of the memory
222     ///     block returned by the `grow_zeroed` call.
223     ///
224     /// # Safety
225     ///
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()`.
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(
247         &mut self,
248         ptr: NonNull<u8>,
249         old_layout: Layout,
250         new_layout: Layout,
251     ) -> Result<NonNull<[u8]>, AllocErr> {
252         debug_assert!(
253             new_layout.size() >= old_layout.size(),
254             "`new_layout.size()` must be greater than or equal to `old_layout.size()`"
255         );
256
257         let new_ptr = self.alloc_zeroed(new_layout)?;
258
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.
264         unsafe {
265             ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), old_layout.size());
266             self.dealloc(ptr, old_layout);
267         }
268
269         Ok(new_ptr)
270     }
271
272     /// Attempts to shrink the memory block.
273     ///
274     /// Returns a new [`NonNull<[u8]>`] containing a pointer and the actual size of the allocated
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.
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
280     /// considered unusable unless it was transferred back to the caller again via the return value
281     /// of this method.
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     ///
286     /// [`NonNull<[u8]>`]: NonNull
287     ///
288     /// # Safety
289     ///
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()`.
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(
311         &mut self,
312         ptr: NonNull<u8>,
313         old_layout: Layout,
314         new_layout: Layout,
315     ) -> Result<NonNull<[u8]>, AllocErr> {
316         debug_assert!(
317             new_layout.size() <= old_layout.size(),
318             "`new_layout.size()` must be smaller than or equal to `old_layout.size()`"
319         );
320
321         let new_ptr = self.alloc(new_layout)?;
322
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.
328         unsafe {
329             ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), new_layout.size());
330             self.dealloc(ptr, old_layout);
331         }
332
333         Ok(new_ptr)
334     }
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)]
340     fn by_ref(&mut self) -> &mut Self {
341         self
342     }
343 }
344
345 #[unstable(feature = "allocator_api", issue = "32838")]
346 unsafe impl<A> AllocRef for &mut A
347 where
348     A: AllocRef + ?Sized,
349 {
350     #[inline]
351     fn alloc(&mut self, layout: Layout) -> Result<NonNull<[u8]>, AllocErr> {
352         (**self).alloc(layout)
353     }
354
355     #[inline]
356     fn alloc_zeroed(&mut self, layout: Layout) -> Result<NonNull<[u8]>, AllocErr> {
357         (**self).alloc_zeroed(layout)
358     }
359
360     #[inline]
361     unsafe fn dealloc(&mut self, ptr: NonNull<u8>, layout: Layout) {
362         // SAFETY: the safety contract must be upheld by the caller
363         unsafe { (**self).dealloc(ptr, layout) }
364     }
365
366     #[inline]
367     unsafe fn grow(
368         &mut self,
369         ptr: NonNull<u8>,
370         old_layout: Layout,
371         new_layout: Layout,
372     ) -> Result<NonNull<[u8]>, AllocErr> {
373         // SAFETY: the safety contract must be upheld by the caller
374         unsafe { (**self).grow(ptr, old_layout, new_layout) }
375     }
376
377     #[inline]
378     unsafe fn grow_zeroed(
379         &mut self,
380         ptr: NonNull<u8>,
381         old_layout: Layout,
382         new_layout: Layout,
383     ) -> Result<NonNull<[u8]>, AllocErr> {
384         // SAFETY: the safety contract must be upheld by the caller
385         unsafe { (**self).grow_zeroed(ptr, old_layout, new_layout) }
386     }
387
388     #[inline]
389     unsafe fn shrink(
390         &mut self,
391         ptr: NonNull<u8>,
392         old_layout: Layout,
393         new_layout: Layout,
394     ) -> Result<NonNull<[u8]>, AllocErr> {
395         // SAFETY: the safety contract must be upheld by the caller
396         unsafe { (**self).shrink(ptr, old_layout, new_layout) }
397     }
398 }