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