about summary refs log tree commit diff
path: root/library/std/src/alloc.rs
diff options
context:
space:
mode:
Diffstat (limited to 'library/std/src/alloc.rs')
-rw-r--r--library/std/src/alloc.rs379
1 files changed, 379 insertions, 0 deletions
diff --git a/library/std/src/alloc.rs b/library/std/src/alloc.rs
new file mode 100644
index 00000000000..ecfaaeace51
--- /dev/null
+++ b/library/std/src/alloc.rs
@@ -0,0 +1,379 @@
+//! Memory allocation APIs
+//!
+//! In a given program, the standard library has one “global” memory allocator
+//! that is used for example by `Box<T>` and `Vec<T>`.
+//!
+//! Currently the default global allocator is unspecified. Libraries, however,
+//! like `cdylib`s and `staticlib`s are guaranteed to use the [`System`] by
+//! default.
+//!
+//! [`System`]: struct.System.html
+//!
+//! # The `#[global_allocator]` attribute
+//!
+//! This attribute allows configuring the choice of global allocator.
+//! You can use this to implement a completely custom global allocator
+//! to route all default allocation requests to a custom object.
+//!
+//! ```rust
+//! use std::alloc::{GlobalAlloc, System, Layout};
+//!
+//! struct MyAllocator;
+//!
+//! unsafe impl GlobalAlloc for MyAllocator {
+//!     unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
+//!         System.alloc(layout)
+//!     }
+//!
+//!     unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
+//!         System.dealloc(ptr, layout)
+//!     }
+//! }
+//!
+//! #[global_allocator]
+//! static GLOBAL: MyAllocator = MyAllocator;
+//!
+//! fn main() {
+//!     // This `Vec` will allocate memory through `GLOBAL` above
+//!     let mut v = Vec::new();
+//!     v.push(1);
+//! }
+//! ```
+//!
+//! The attribute is used on a `static` item whose type implements the
+//! [`GlobalAlloc`] trait. This type can be provided by an external library:
+//!
+//! [`GlobalAlloc`]: ../../core/alloc/trait.GlobalAlloc.html
+//!
+//! ```rust,ignore (demonstrates crates.io usage)
+//! extern crate jemallocator;
+//!
+//! use jemallocator::Jemalloc;
+//!
+//! #[global_allocator]
+//! static GLOBAL: Jemalloc = Jemalloc;
+//!
+//! fn main() {}
+//! ```
+//!
+//! The `#[global_allocator]` can only be used once in a crate
+//! or its recursive dependencies.
+
+#![deny(unsafe_op_in_unsafe_fn)]
+#![stable(feature = "alloc_module", since = "1.28.0")]
+
+use core::intrinsics;
+use core::ptr::NonNull;
+use core::sync::atomic::{AtomicPtr, Ordering};
+use core::{mem, ptr};
+
+use crate::sys_common::util::dumb_print;
+
+#[stable(feature = "alloc_module", since = "1.28.0")]
+#[doc(inline)]
+pub use alloc_crate::alloc::*;
+
+/// The default memory allocator provided by the operating system.
+///
+/// This is based on `malloc` on Unix platforms and `HeapAlloc` on Windows,
+/// plus related functions.
+///
+/// This type implements the `GlobalAlloc` trait and Rust programs by default
+/// work as if they had this definition:
+///
+/// ```rust
+/// use std::alloc::System;
+///
+/// #[global_allocator]
+/// static A: System = System;
+///
+/// fn main() {
+///     let a = Box::new(4); // Allocates from the system allocator.
+///     println!("{}", a);
+/// }
+/// ```
+///
+/// You can also define your own wrapper around `System` if you'd like, such as
+/// keeping track of the number of all bytes allocated:
+///
+/// ```rust
+/// use std::alloc::{System, GlobalAlloc, Layout};
+/// use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
+///
+/// struct Counter;
+///
+/// static ALLOCATED: AtomicUsize = AtomicUsize::new(0);
+///
+/// unsafe impl GlobalAlloc for Counter {
+///     unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
+///         let ret = System.alloc(layout);
+///         if !ret.is_null() {
+///             ALLOCATED.fetch_add(layout.size(), SeqCst);
+///         }
+///         return ret
+///     }
+///
+///     unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
+///         System.dealloc(ptr, layout);
+///         ALLOCATED.fetch_sub(layout.size(), SeqCst);
+///     }
+/// }
+///
+/// #[global_allocator]
+/// static A: Counter = Counter;
+///
+/// fn main() {
+///     println!("allocated bytes before main: {}", ALLOCATED.load(SeqCst));
+/// }
+/// ```
+///
+/// It can also be used directly to allocate memory independently of whatever
+/// global allocator has been selected for a Rust program. For example if a Rust
+/// program opts in to using jemalloc as the global allocator, `System` will
+/// still allocate memory using `malloc` and `HeapAlloc`.
+#[stable(feature = "alloc_system_type", since = "1.28.0")]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct System;
+
+// The AllocRef impl checks the layout size to be non-zero and forwards to the GlobalAlloc impl,
+// which is in `std::sys::*::alloc`.
+#[unstable(feature = "allocator_api", issue = "32838")]
+unsafe impl AllocRef for System {
+    #[inline]
+    fn alloc(&mut self, layout: Layout, init: AllocInit) -> Result<MemoryBlock, AllocErr> {
+        unsafe {
+            let size = layout.size();
+            if size == 0 {
+                Ok(MemoryBlock { ptr: layout.dangling(), size: 0 })
+            } else {
+                let raw_ptr = match init {
+                    AllocInit::Uninitialized => GlobalAlloc::alloc(self, layout),
+                    AllocInit::Zeroed => GlobalAlloc::alloc_zeroed(self, layout),
+                };
+                let ptr = NonNull::new(raw_ptr).ok_or(AllocErr)?;
+                Ok(MemoryBlock { ptr, size })
+            }
+        }
+    }
+
+    #[inline]
+    unsafe fn dealloc(&mut self, ptr: NonNull<u8>, layout: Layout) {
+        if layout.size() != 0 {
+            // SAFETY: The safety guarantees are explained in the documentation
+            // for the `GlobalAlloc` trait and its `dealloc` method.
+            unsafe { GlobalAlloc::dealloc(self, ptr.as_ptr(), layout) }
+        }
+    }
+
+    #[inline]
+    unsafe fn grow(
+        &mut self,
+        ptr: NonNull<u8>,
+        layout: Layout,
+        new_size: usize,
+        placement: ReallocPlacement,
+        init: AllocInit,
+    ) -> Result<MemoryBlock, AllocErr> {
+        let size = layout.size();
+        debug_assert!(
+            new_size >= size,
+            "`new_size` must be greater than or equal to `memory.size()`"
+        );
+
+        if size == new_size {
+            return Ok(MemoryBlock { ptr, size });
+        }
+
+        match placement {
+            ReallocPlacement::InPlace => Err(AllocErr),
+            ReallocPlacement::MayMove if layout.size() == 0 => {
+                let new_layout =
+                    // SAFETY: The new size and layout alignement guarantees
+                    // are transfered to the caller (they come from parameters).
+                    //
+                    // See the preconditions for `Layout::from_size_align` to
+                    // see what must be checked.
+                    unsafe { Layout::from_size_align_unchecked(new_size, layout.align()) };
+                self.alloc(new_layout, init)
+            }
+            ReallocPlacement::MayMove => {
+                // SAFETY:
+                //
+                // The safety guarantees are explained in the documentation
+                // for the `GlobalAlloc` trait and its `dealloc` method.
+                //
+                // `realloc` probably checks for `new_size > size` or something
+                // similar.
+                //
+                // For the guarantees about `init_offset`, see its documentation:
+                // `ptr` is assumed valid (and checked for non-NUL) and
+                // `memory.size` is set to `new_size` so the offset being `size`
+                // is valid.
+                let memory = unsafe {
+                    intrinsics::assume(new_size > size);
+                    let ptr = GlobalAlloc::realloc(self, ptr.as_ptr(), layout, new_size);
+                    let memory =
+                        MemoryBlock { ptr: NonNull::new(ptr).ok_or(AllocErr)?, size: new_size };
+                    init.init_offset(memory, size);
+                    memory
+                };
+                Ok(memory)
+            }
+        }
+    }
+
+    #[inline]
+    unsafe fn shrink(
+        &mut self,
+        ptr: NonNull<u8>,
+        layout: Layout,
+        new_size: usize,
+        placement: ReallocPlacement,
+    ) -> Result<MemoryBlock, AllocErr> {
+        let size = layout.size();
+        debug_assert!(
+            new_size <= size,
+            "`new_size` must be smaller than or equal to `memory.size()`"
+        );
+
+        if size == new_size {
+            return Ok(MemoryBlock { ptr, size });
+        }
+
+        match placement {
+            ReallocPlacement::InPlace => Err(AllocErr),
+            ReallocPlacement::MayMove if new_size == 0 => {
+                // SAFETY: see `GlobalAlloc::dealloc` for the guarantees that
+                // must be respected. `ptr` and `layout` are parameters and so
+                // those guarantees must be checked by the caller.
+                unsafe { self.dealloc(ptr, layout) };
+                Ok(MemoryBlock { ptr: layout.dangling(), size: 0 })
+            }
+            ReallocPlacement::MayMove => {
+                // SAFETY:
+                //
+                // See `GlobalAlloc::realloc` for more informations about the
+                // guarantees expected by this method. `ptr`, `layout` and
+                // `new_size` are parameters and the responsability for their
+                // correctness is left to the caller.
+                //
+                // `realloc` probably checks for `new_size < size` or something
+                // similar.
+                let memory = unsafe {
+                    intrinsics::assume(new_size < size);
+                    let ptr = GlobalAlloc::realloc(self, ptr.as_ptr(), layout, new_size);
+                    MemoryBlock { ptr: NonNull::new(ptr).ok_or(AllocErr)?, size: new_size }
+                };
+                Ok(memory)
+            }
+        }
+    }
+}
+static HOOK: AtomicPtr<()> = AtomicPtr::new(ptr::null_mut());
+
+/// Registers a custom allocation error hook, replacing any that was previously registered.
+///
+/// The allocation error hook is invoked when an infallible memory allocation fails, before
+/// the runtime aborts. The default hook prints a message to standard error,
+/// but this behavior can be customized with the [`set_alloc_error_hook`] and
+/// [`take_alloc_error_hook`] functions.
+///
+/// The hook is provided with a `Layout` struct which contains information
+/// about the allocation that failed.
+///
+/// The allocation error hook is a global resource.
+///
+/// [`set_alloc_error_hook`]: fn.set_alloc_error_hook.html
+/// [`take_alloc_error_hook`]: fn.take_alloc_error_hook.html
+#[unstable(feature = "alloc_error_hook", issue = "51245")]
+pub fn set_alloc_error_hook(hook: fn(Layout)) {
+    HOOK.store(hook as *mut (), Ordering::SeqCst);
+}
+
+/// Unregisters the current allocation error hook, returning it.
+///
+/// *See also the function [`set_alloc_error_hook`].*
+///
+/// If no custom hook is registered, the default hook will be returned.
+///
+/// [`set_alloc_error_hook`]: fn.set_alloc_error_hook.html
+#[unstable(feature = "alloc_error_hook", issue = "51245")]
+pub fn take_alloc_error_hook() -> fn(Layout) {
+    let hook = HOOK.swap(ptr::null_mut(), Ordering::SeqCst);
+    if hook.is_null() { default_alloc_error_hook } else { unsafe { mem::transmute(hook) } }
+}
+
+fn default_alloc_error_hook(layout: Layout) {
+    dumb_print(format_args!("memory allocation of {} bytes failed", layout.size()));
+}
+
+#[cfg(not(test))]
+#[doc(hidden)]
+#[alloc_error_handler]
+#[unstable(feature = "alloc_internals", issue = "none")]
+pub fn rust_oom(layout: Layout) -> ! {
+    let hook = HOOK.load(Ordering::SeqCst);
+    let hook: fn(Layout) =
+        if hook.is_null() { default_alloc_error_hook } else { unsafe { mem::transmute(hook) } };
+    hook(layout);
+    crate::process::abort()
+}
+
+#[cfg(not(test))]
+#[doc(hidden)]
+#[allow(unused_attributes)]
+#[unstable(feature = "alloc_internals", issue = "none")]
+pub mod __default_lib_allocator {
+    use super::{GlobalAlloc, Layout, System};
+    // These magic symbol names are used as a fallback for implementing the
+    // `__rust_alloc` etc symbols (see `src/liballoc/alloc.rs`) when there is
+    // no `#[global_allocator]` attribute.
+
+    // for symbol names src/librustc_ast/expand/allocator.rs
+    // for signatures src/librustc_allocator/lib.rs
+
+    // linkage directives are provided as part of the current compiler allocator
+    // ABI
+
+    #[rustc_std_internal_symbol]
+    pub unsafe extern "C" fn __rdl_alloc(size: usize, align: usize) -> *mut u8 {
+        // SAFETY: see the guarantees expected by `Layout::from_size_align` and
+        // `GlobalAlloc::alloc`.
+        unsafe {
+            let layout = Layout::from_size_align_unchecked(size, align);
+            System.alloc(layout)
+        }
+    }
+
+    #[rustc_std_internal_symbol]
+    pub unsafe extern "C" fn __rdl_dealloc(ptr: *mut u8, size: usize, align: usize) {
+        // SAFETY: see the guarantees expected by `Layout::from_size_align` and
+        // `GlobalAlloc::dealloc`.
+        unsafe { System.dealloc(ptr, Layout::from_size_align_unchecked(size, align)) }
+    }
+
+    #[rustc_std_internal_symbol]
+    pub unsafe extern "C" fn __rdl_realloc(
+        ptr: *mut u8,
+        old_size: usize,
+        align: usize,
+        new_size: usize,
+    ) -> *mut u8 {
+        // SAFETY: see the guarantees expected by `Layout::from_size_align` and
+        // `GlobalAlloc::realloc`.
+        unsafe {
+            let old_layout = Layout::from_size_align_unchecked(old_size, align);
+            System.realloc(ptr, old_layout, new_size)
+        }
+    }
+
+    #[rustc_std_internal_symbol]
+    pub unsafe extern "C" fn __rdl_alloc_zeroed(size: usize, align: usize) -> *mut u8 {
+        // SAFETY: see the guarantees expected by `Layout::from_size_align` and
+        // `GlobalAlloc::alloc_zeroed`.
+        unsafe {
+            let layout = Layout::from_size_align_unchecked(size, align);
+            System.alloc_zeroed(layout)
+        }
+    }
+}