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