]> git.lizzy.rs Git - rust.git/blob - src/libcore/alloc/mod.rs
f2f12a98fa61b3ebb33304e701f9fe850dd010f7
[rust.git] / src / libcore / 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(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 /// A desired initial state for allocated memory.
33 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
34 #[unstable(feature = "allocator_api", issue = "32838")]
35 pub enum AllocInit {
36     /// The contents of the new memory are undefined.
37     ///
38     /// Reading uninitialized memory is Undefined Behavior; it must be initialized before use.
39     Uninitialized,
40     /// The new memory is guaranteed to be zeroed.
41     Zeroed,
42 }
43
44 /// Represents a block of allocated memory returned by an allocator.
45 #[derive(Debug, Copy, Clone)]
46 #[unstable(feature = "allocator_api", issue = "32838")]
47 pub struct MemoryBlock {
48     pub ptr: NonNull<u8>,
49     pub size: usize,
50 }
51
52 impl MemoryBlock {
53     /// Initialize the memory block like specified by `init`.
54     ///
55     /// This behaves like calling [`MemoryBlock::initialize_offset(ptr, layout, 0)`][off].
56     ///
57     /// [off]: MemoryBlock::init_offset
58     ///
59     /// [*fit*]: trait.AllocRef.html#memory-fitting
60     #[inline]
61     #[unstable(feature = "allocator_api", issue = "32838")]
62     pub fn init(&mut self, init: AllocInit) {
63         // SAFETY: 0 is always smaller or equal to the size
64         unsafe { self.init_offset(init, 0) }
65     }
66
67     /// Initialize the memory block like specified by `init` at the specified `offset`.
68     ///
69     /// This is a no-op for [`AllocInit::Uninitialized`] and writes zeroes for [`AllocInit::Zeroed`]
70     /// at `ptr + offset` until `ptr + layout.size()`.
71     ///
72     /// # Safety
73     ///
74     /// * `offset` must be smaller than or equal to `size()`
75     ///
76     /// [*fit*]: trait.AllocRef.html#memory-fitting
77     #[inline]
78     #[unstable(feature = "allocator_api", issue = "32838")]
79     pub unsafe fn init_offset(&mut self, init: AllocInit, offset: usize) {
80         debug_assert!(offset <= self.size, "`offset` must be smaller than or equal to `size()`");
81         match init {
82             AllocInit::Uninitialized => (),
83             AllocInit::Zeroed => self.ptr.as_ptr().add(offset).write_bytes(0, self.size - offset),
84         }
85     }
86 }
87
88 /// A placement constraint when growing or shrinking an existing allocation.
89 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
90 #[unstable(feature = "allocator_api", issue = "32838")]
91 pub enum ReallocPlacement {
92     /// The allocator is allowed to move the allocation to a different memory address.
93     // FIXME(wg-allocators#46): Add a section to the module documentation "What is a legal
94     //                          allocator" and link it at "valid location".
95     ///
96     /// If the allocation _does_ move, it's the responsibility of the allocator
97     /// to also move the data from the previous location to the new location.
98     MayMove,
99     /// The address of the new memory must not change.
100     ///
101     /// If the allocation would have to be moved to a new location to fit, the
102     /// reallocation request will fail.
103     InPlace,
104 }
105
106 /// An implementation of `AllocRef` can allocate, grow, shrink, and deallocate arbitrary blocks of
107 /// data described via [`Layout`][].
108 ///
109 /// `AllocRef` is designed to be implemented on ZSTs, references, or smart pointers because having
110 /// an allocator like `MyAlloc([u8; N])` cannot be moved, without updating the pointers to the
111 /// allocated memory.
112 ///
113 /// Unlike [`GlobalAlloc`][], zero-sized allocations are allowed in `AllocRef`. If an underlying
114 /// allocator does not support this (like jemalloc) or return a null pointer (such as
115 /// `libc::malloc`), this case must be caught.
116 ///
117 /// ### Currently allocated memory
118 ///
119 /// Some of the methods require that a memory block be *currently allocated* via an allocator. This
120 /// means that:
121 ///
122 /// * the starting address for that memory block was previously returned by [`alloc`], [`grow`], or
123 ///   [`shrink`], and
124 ///
125 /// * the memory block has not been subsequently deallocated, where blocks are either deallocated
126 ///   directly by being passed to [`dealloc`] or were changed by being passed to [`grow`] or
127 ///   [`shrink`] that returns `Ok`. If `grow` or `shrink` have returned `Err`, the passed pointer
128 ///   remains valid.
129 ///
130 /// [`alloc`]: AllocRef::alloc
131 /// [`grow`]: AllocRef::grow
132 /// [`shrink`]: AllocRef::shrink
133 /// [`dealloc`]: AllocRef::dealloc
134 ///
135 /// ### Memory fitting
136 ///
137 /// Some of the methods require that a layout *fit* a memory block. What it means for a layout to
138 /// "fit" a memory block means (or equivalently, for a memory block to "fit" a layout) is that the
139 /// following conditions must hold:
140 ///
141 /// * The block must be allocated with the same alignment as [`layout.align()`], and
142 ///
143 /// * The provided [`layout.size()`] must fall in the range `min ..= max`, where:
144 ///   - `min` is the size of the layout most recently used to allocate the block, and
145 ///   - `max` is the latest actual size returned from [`alloc`], [`grow`], or [`shrink`].
146 ///
147 /// [`layout.align()`]: Layout::align
148 /// [`layout.size()`]: Layout::size
149 ///
150 /// # Safety
151 ///
152 /// * Memory blocks returned from an allocator must point to valid memory and retain their validity
153 ///   until the instance and all of its clones are dropped, and
154 ///
155 /// * cloning or moving the allocator must not invalidate memory blocks returned from this
156 ///   allocator. A cloned allocator must behave like the same allocator.
157 ///
158 /// * any pointer to a memory block which is [*currently allocated*] may be passed to any other
159 ///   method of the allocator.
160 ///
161 /// [*currently allocated*]: #currently-allocated-memory
162 #[unstable(feature = "allocator_api", issue = "32838")]
163 pub unsafe trait AllocRef {
164     /// On success, returns a memory block meeting the size and alignment guarantees of `layout`.
165     ///
166     /// The returned block may have a larger size than specified by `layout.size()` and is
167     /// initialized as specified by [`init`], all the way up to the returned size of the block.
168     ///
169     /// [`init`]: AllocInit
170     ///
171     /// # Errors
172     ///
173     /// Returning `Err` indicates that either memory is exhausted or `layout` does not meet
174     /// allocator's size or alignment constraints.
175     ///
176     /// Implementations are encouraged to return `Err` on memory exhaustion rather than panicking or
177     /// aborting, but this is not a strict requirement. (Specifically: it is *legal* to implement
178     /// this trait atop an underlying native allocation library that aborts on memory exhaustion.)
179     ///
180     /// Clients wishing to abort computation in response to an allocation error are encouraged to
181     /// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar.
182     ///
183     /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
184     fn alloc(&mut self, layout: Layout, init: AllocInit) -> Result<MemoryBlock, AllocErr>;
185
186     /// Deallocates the memory denoted by `memory`.
187     ///
188     /// # Safety
189     ///
190     /// `memory` must be a memory block returned by this allocator.
191     unsafe fn dealloc(&mut self, ptr: NonNull<u8>, layout: Layout);
192
193     /// Attempts to extend the memory block.
194     ///
195     /// Returns a new memory block containing a pointer and the actual size of the allocated
196     /// block. The pointer is suitable for holding data described by a new layout with `layout`’s
197     /// alignment and a size given by `new_size`. To accomplish this, the allocator may extend the
198     /// allocation referenced by `ptr` to fit the new layout. If the [`placement`] is
199     /// [`InPlace`], the returned pointer is guaranteed to be the same as the passed `ptr`.
200     ///
201     /// If `ReallocPlacement::MayMove` is used then ownership of the memory block referenced by `ptr`
202     /// is transferred to this allocator. The memory may or may not be freed, and should be
203     /// considered unusable (unless of course it is transferred back to the caller again via the
204     /// return value of this method).
205     ///
206     /// If this method returns `Err`, then ownership of the memory block has not been transferred to
207     /// this allocator, and the contents of the memory block are unaltered.
208     ///
209     /// The memory block will contain the following contents after a successful call to `grow`:
210     ///   * Bytes `0..layout.size()` are preserved from the original allocation.
211     ///   * Bytes `layout.size()..old_size` will either be preserved or initialized according to
212     ///     [`init`], depending on the allocator implementation. `old_size` refers to the size of
213     ///     the `MemoryBlock` prior to the `grow` call, which may be larger than the size
214     ///     that was originally requested when it was allocated.
215     ///   * Bytes `old_size..new_size` are initialized according to [`init`]. `new_size` refers to
216     ///     the size of the `MemoryBlock` returned by the `grow` call.
217     ///
218     /// [`InPlace`]: ReallocPlacement::InPlace
219     /// [`placement`]: ReallocPlacement
220     /// [`init`]: AllocInit
221     ///
222     /// # Safety
223     ///
224     /// * `ptr` must be [*currently allocated*] via this allocator,
225     /// * `layout` must [*fit*] the `ptr`. (The `new_size` argument need not fit it.)
226     // We can't require that `new_size` is strictly greater than `memory.size` because of ZSTs.
227     // An alternative would be
228     // * `new_size must be strictly greater than `memory.size` or both are zero
229     /// * `new_size` must be greater than or equal to `layout.size()`
230     /// * `new_size`, when rounded up to the nearest multiple of `layout.align()`, must not overflow
231     ///   (i.e., the rounded value must be less than `usize::MAX`).
232     ///
233     /// [*currently allocated*]: #currently-allocated-memory
234     /// [*fit*]: #memory-fitting
235     ///
236     /// # Errors
237     ///
238     /// Returns `Err` if the new layout does not meet the allocator's size and alignment
239     /// constraints of the allocator, or if growing otherwise fails.
240     ///
241     /// Implementations are encouraged to return `Err` on memory exhaustion rather than panicking or
242     /// aborting, but this is not a strict requirement. (Specifically: it is *legal* to implement
243     /// this trait atop an underlying native allocation library that aborts on memory exhaustion.)
244     ///
245     /// Clients wishing to abort computation in response to an allocation error are encouraged to
246     /// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar.
247     ///
248     /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
249     unsafe fn grow(
250         &mut self,
251         ptr: NonNull<u8>,
252         layout: Layout,
253         new_size: usize,
254         placement: ReallocPlacement,
255         init: AllocInit,
256     ) -> Result<MemoryBlock, AllocErr> {
257         match placement {
258             ReallocPlacement::InPlace => Err(AllocErr),
259             ReallocPlacement::MayMove => {
260                 let size = layout.size();
261                 debug_assert!(
262                     new_size >= size,
263                     "`new_size` must be greater than or equal to `layout.size()`"
264                 );
265
266                 if new_size == size {
267                     return Ok(MemoryBlock { ptr, size });
268                 }
269
270                 let new_layout = Layout::from_size_align_unchecked(new_size, layout.align());
271                 let new_memory = self.alloc(new_layout, init)?;
272                 ptr::copy_nonoverlapping(ptr.as_ptr(), new_memory.ptr.as_ptr(), size);
273                 self.dealloc(ptr, layout);
274                 Ok(new_memory)
275             }
276         }
277     }
278
279     /// Attempts to shrink the memory block.
280     ///
281     /// Returns a new memory block containing a pointer and the actual size of the allocated
282     /// block. The pointer is suitable for holding data described by a new layout with `layout`’s
283     /// alignment and a size given by `new_size`. To accomplish this, the allocator may shrink the
284     /// allocation referenced by `ptr` to fit the new layout. If the [`placement`] is
285     /// [`InPlace`], the returned pointer is guaranteed to be the same as the passed `ptr`.
286     ///
287     /// If this returns `Ok`, then ownership of the memory block referenced by `ptr` has been
288     /// transferred to this allocator. The memory may or may not have been freed, and should be
289     /// considered unusable unless it was transferred back to the caller again via the
290     /// return value of this method.
291     ///
292     /// If this method returns `Err`, then ownership of the memory block has not been transferred to
293     /// this allocator, and the contents of the memory block are unaltered.
294     ///
295     /// The behavior of how the allocator tries to shrink the memory is specified by [`placement`].
296     ///
297     /// [`InPlace`]: ReallocPlacement::InPlace
298     /// [`placement`]: ReallocPlacement
299     ///
300     /// # Safety
301     ///
302     /// * `ptr` must be [*currently allocated*] via this allocator,
303     /// * `layout` must [*fit*] the `ptr`. (The `new_size` argument need not fit it.)
304     // We can't require that `new_size` is strictly smaller than `memory.size` because of ZSTs.
305     // An alternative would be
306     // * `new_size must be strictly smaller than `memory.size` or both are zero
307     /// * `new_size` must be smaller than or equal to `layout.size()`
308     ///
309     /// [*currently allocated*]: #currently-allocated-memory
310     /// [*fit*]: #memory-fitting
311     ///
312     /// # Errors
313     ///
314     /// Returns `Err` if the new layout does not meet the allocator's size and alignment
315     /// constraints of the allocator, or if growing otherwise fails.
316     ///
317     /// Implementations are encouraged to return `Err` on memory exhaustion rather than panicking or
318     /// aborting, but this is not a strict requirement. (Specifically: it is *legal* to implement
319     /// this trait atop an underlying native allocation library that aborts on memory exhaustion.)
320     ///
321     /// Clients wishing to abort computation in response to an allocation error are encouraged to
322     /// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar.
323     ///
324     /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
325     unsafe fn shrink(
326         &mut self,
327         ptr: NonNull<u8>,
328         layout: Layout,
329         new_size: usize,
330         placement: ReallocPlacement,
331     ) -> Result<MemoryBlock, AllocErr> {
332         match placement {
333             ReallocPlacement::InPlace => Err(AllocErr),
334             ReallocPlacement::MayMove => {
335                 let size = layout.size();
336                 debug_assert!(
337                     new_size <= size,
338                     "`new_size` must be smaller than or equal to `layout.size()`"
339                 );
340
341                 if new_size == size {
342                     return Ok(MemoryBlock { ptr, size });
343                 }
344
345                 let new_layout = Layout::from_size_align_unchecked(new_size, layout.align());
346                 let new_memory = self.alloc(new_layout, AllocInit::Uninitialized)?;
347                 ptr::copy_nonoverlapping(ptr.as_ptr(), new_memory.ptr.as_ptr(), new_size);
348                 self.dealloc(ptr, layout);
349                 Ok(new_memory)
350             }
351         }
352     }
353 }