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