]> git.lizzy.rs Git - rust.git/blob - library/core/src/alloc/global.rs
Rollup merge of #82500 - CDirkx:hermit-pipe, r=joshtriplett
[rust.git] / library / core / src / alloc / global.rs
1 use crate::alloc::Layout;
2 use crate::cmp;
3 use crate::ptr;
4
5 /// A memory allocator that can be registered as the standard library’s default
6 /// through the `#[global_allocator]` attribute.
7 ///
8 /// Some of the methods require that a memory block be *currently
9 /// allocated* via an allocator. This means that:
10 ///
11 /// * the starting address for that memory block was previously
12 ///   returned by a previous call to an allocation method
13 ///   such as `alloc`, and
14 ///
15 /// * the memory block has not been subsequently deallocated, where
16 ///   blocks are deallocated either by being passed to a deallocation
17 ///   method such as `dealloc` or by being
18 ///   passed to a reallocation method that returns a non-null pointer.
19 ///
20 ///
21 /// # Example
22 ///
23 /// ```no_run
24 /// use std::alloc::{GlobalAlloc, Layout, alloc};
25 /// use std::ptr::null_mut;
26 ///
27 /// struct MyAllocator;
28 ///
29 /// unsafe impl GlobalAlloc for MyAllocator {
30 ///     unsafe fn alloc(&self, _layout: Layout) -> *mut u8 { null_mut() }
31 ///     unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {}
32 /// }
33 ///
34 /// #[global_allocator]
35 /// static A: MyAllocator = MyAllocator;
36 ///
37 /// fn main() {
38 ///     unsafe {
39 ///         assert!(alloc(Layout::new::<u32>()).is_null())
40 ///     }
41 /// }
42 /// ```
43 ///
44 /// # Safety
45 ///
46 /// The `GlobalAlloc` trait is an `unsafe` trait for a number of reasons, and
47 /// implementors must ensure that they adhere to these contracts:
48 ///
49 /// * It's undefined behavior if global allocators unwind. This restriction may
50 ///   be lifted in the future, but currently a panic from any of these
51 ///   functions may lead to memory unsafety.
52 ///
53 /// * `Layout` queries and calculations in general must be correct. Callers of
54 ///   this trait are allowed to rely on the contracts defined on each method,
55 ///   and implementors must ensure such contracts remain true.
56 ///
57 /// * You may not rely on allocations actually happening, even if there are explicit
58 ///   heap allocations in the source. The optimizer may detect unused allocations that it can either
59 ///   eliminate entirely or move to the stack and thus never invoke the allocator. The
60 ///   optimizer may further assume that allocation is infallible, so code that used to fail due
61 ///   to allocator failures may now suddenly work because the optimizer worked around the
62 ///   need for an allocation. More concretely, the following code example is unsound, irrespective
63 ///   of whether your custom allocator allows counting how many allocations have happened.
64 ///
65 ///   ```rust,ignore (unsound and has placeholders)
66 ///   drop(Box::new(42));
67 ///   let number_of_heap_allocs = /* call private allocator API */;
68 ///   unsafe { std::intrinsics::assume(number_of_heap_allocs > 0); }
69 ///   ```
70 ///
71 ///   Note that the optimizations mentioned above are not the only
72 ///   optimization that can be applied. You may generally not rely on heap allocations
73 ///   happening if they can be removed without changing program behavior.
74 ///   Whether allocations happen or not is not part of the program behavior, even if it
75 ///   could be detected via an allocator that tracks allocations by printing or otherwise
76 ///   having side effects.
77 #[stable(feature = "global_alloc", since = "1.28.0")]
78 pub unsafe trait GlobalAlloc {
79     /// Allocate memory as described by the given `layout`.
80     ///
81     /// Returns a pointer to newly-allocated memory,
82     /// or null to indicate allocation failure.
83     ///
84     /// # Safety
85     ///
86     /// This function is unsafe because undefined behavior can result
87     /// if the caller does not ensure that `layout` has non-zero size.
88     ///
89     /// (Extension subtraits might provide more specific bounds on
90     /// behavior, e.g., guarantee a sentinel address or a null pointer
91     /// in response to a zero-size allocation request.)
92     ///
93     /// The allocated block of memory may or may not be initialized.
94     ///
95     /// # Errors
96     ///
97     /// Returning a null pointer indicates that either memory is exhausted
98     /// or `layout` does not meet this allocator's size or alignment constraints.
99     ///
100     /// Implementations are encouraged to return null on memory
101     /// exhaustion rather than aborting, but this is not
102     /// a strict requirement. (Specifically: it is *legal* to
103     /// implement this trait atop an underlying native allocation
104     /// library that aborts on memory exhaustion.)
105     ///
106     /// Clients wishing to abort computation in response to an
107     /// allocation error are encouraged to call the [`handle_alloc_error`] function,
108     /// rather than directly invoking `panic!` or similar.
109     ///
110     /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
111     #[stable(feature = "global_alloc", since = "1.28.0")]
112     unsafe fn alloc(&self, layout: Layout) -> *mut u8;
113
114     /// Deallocate the block of memory at the given `ptr` pointer with the given `layout`.
115     ///
116     /// # Safety
117     ///
118     /// This function is unsafe because undefined behavior can result
119     /// if the caller does not ensure all of the following:
120     ///
121     /// * `ptr` must denote a block of memory currently allocated via
122     ///   this allocator,
123     ///
124     /// * `layout` must be the same layout that was used
125     ///   to allocate that block of memory.
126     #[stable(feature = "global_alloc", since = "1.28.0")]
127     unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout);
128
129     /// Behaves like `alloc`, but also ensures that the contents
130     /// are set to zero before being returned.
131     ///
132     /// # Safety
133     ///
134     /// This function is unsafe for the same reasons that `alloc` is.
135     /// However the allocated block of memory is guaranteed to be initialized.
136     ///
137     /// # Errors
138     ///
139     /// Returning a null pointer indicates that either memory is exhausted
140     /// or `layout` does not meet allocator's size or alignment constraints,
141     /// just as in `alloc`.
142     ///
143     /// Clients wishing to abort computation in response to an
144     /// allocation error are encouraged to call the [`handle_alloc_error`] function,
145     /// rather than directly invoking `panic!` or similar.
146     ///
147     /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
148     #[stable(feature = "global_alloc", since = "1.28.0")]
149     unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
150         let size = layout.size();
151         // SAFETY: the safety contract for `alloc` must be upheld by the caller.
152         let ptr = unsafe { self.alloc(layout) };
153         if !ptr.is_null() {
154             // SAFETY: as allocation succeeded, the region from `ptr`
155             // of size `size` is guaranteed to be valid for writes.
156             unsafe { ptr::write_bytes(ptr, 0, size) };
157         }
158         ptr
159     }
160
161     /// Shrink or grow a block of memory to the given `new_size`.
162     /// The block is described by the given `ptr` pointer and `layout`.
163     ///
164     /// If this returns a non-null pointer, then ownership of the memory block
165     /// referenced by `ptr` has been transferred to this allocator.
166     /// The memory may or may not have been deallocated,
167     /// and should be considered unusable (unless of course it was
168     /// transferred back to the caller again via the return value of
169     /// this method). The new memory block is allocated with `layout`, but
170     /// with the `size` updated to `new_size`. This new layout should be
171     /// used when deallocating the new memory block with `dealloc`. The range
172     /// `0..min(layout.size(), new_size)` of the new memory block is
173     /// guaranteed to have the same values as the original block.
174     ///
175     /// If this method returns null, then ownership of the memory
176     /// block has not been transferred to this allocator, and the
177     /// contents of the memory block are unaltered.
178     ///
179     /// # Safety
180     ///
181     /// This function is unsafe because undefined behavior can result
182     /// if the caller does not ensure all of the following:
183     ///
184     /// * `ptr` must be currently allocated via this allocator,
185     ///
186     /// * `layout` must be the same layout that was used
187     ///   to allocate that block of memory,
188     ///
189     /// * `new_size` must be greater than zero.
190     ///
191     /// * `new_size`, when rounded up to the nearest multiple of `layout.align()`,
192     ///   must not overflow (i.e., the rounded value must be less than `usize::MAX`).
193     ///
194     /// (Extension subtraits might provide more specific bounds on
195     /// behavior, e.g., guarantee a sentinel address or a null pointer
196     /// in response to a zero-size allocation request.)
197     ///
198     /// # Errors
199     ///
200     /// Returns null if the new layout does not meet the size
201     /// and alignment constraints of the allocator, or if reallocation
202     /// otherwise fails.
203     ///
204     /// Implementations are encouraged to return null on memory
205     /// exhaustion rather than panicking or aborting, but this is not
206     /// a strict requirement. (Specifically: it is *legal* to
207     /// implement this trait atop an underlying native allocation
208     /// library that aborts on memory exhaustion.)
209     ///
210     /// Clients wishing to abort computation in response to a
211     /// reallocation error are encouraged to call the [`handle_alloc_error`] function,
212     /// rather than directly invoking `panic!` or similar.
213     ///
214     /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
215     #[stable(feature = "global_alloc", since = "1.28.0")]
216     unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
217         // SAFETY: the caller must ensure that the `new_size` does not overflow.
218         // `layout.align()` comes from a `Layout` and is thus guaranteed to be valid.
219         let new_layout = unsafe { Layout::from_size_align_unchecked(new_size, layout.align()) };
220         // SAFETY: the caller must ensure that `new_layout` is greater than zero.
221         let new_ptr = unsafe { self.alloc(new_layout) };
222         if !new_ptr.is_null() {
223             // SAFETY: the previously allocated block cannot overlap the newly allocated block.
224             // The safety contract for `dealloc` must be upheld by the caller.
225             unsafe {
226                 ptr::copy_nonoverlapping(ptr, new_ptr, cmp::min(layout.size(), new_size));
227                 self.dealloc(ptr, layout);
228             }
229         }
230         new_ptr
231     }
232 }