//! A Rust port of the `dlmalloc` allocator. //! //! The `dlmalloc` allocator is described at //! http://g.oswego.edu/dl/html/malloc.html and this Rust crate is a straight //! port of the C code for the allocator into Rust. The implementation is //! wrapped up in a `Dlmalloc` type and has support for Linux, OSX, and Wasm //! currently. //! //! The primary purpose of this crate is that it serves as the default memory //! allocator for the `wasm32-unknown-unknown` target in the standard library. //! Support for other platforms is largely untested and unused, but is used when //! testing this crate. #![allow(dead_code)] #![no_std] #![deny(missing_docs)] #![cfg_attr(target_arch = "wasm64", feature(simd_wasm64))] use core::cmp; use core::ptr; use sys::System; #[cfg(feature = "global")] pub use self::global::{enable_alloc_after_fork, GlobalDlmalloc}; mod dlmalloc; #[cfg(feature = "global")] mod global; /// In order for this crate to efficiently manage memory, it needs a way to communicate with the /// underlying platform. This `Allocator` trait provides an interface for this communication. pub unsafe trait Allocator: Send { /// Allocates system memory region of at least `size` bytes /// Returns a triple of `(base, size, flags)` where `base` is a pointer to the beginning of the /// allocated memory region. `size` is the actual size of the region while `flags` specifies /// properties of the allocated region. If `EXTERN_BIT` (bit 0) set in flags, then we did not /// allocate this segment and so should not try to deallocate or merge with others. /// This function can return a `std::ptr::null_mut()` when allocation fails (other values of /// the triple will be ignored). fn alloc(&self, size: usize) -> (*mut u8, usize, u32); /// Remaps system memory region at `ptr` with size `oldsize` to a potential new location with /// size `newsize`. `can_move` indicates if the location is allowed to move to a completely new /// location, or that it is only allowed to change in size. Returns a pointer to the new /// location in memory. /// This function can return a `std::ptr::null_mut()` to signal an error. fn remap(&self, ptr: *mut u8, oldsize: usize, newsize: usize, can_move: bool) -> *mut u8; /// Frees a part of a memory chunk. The original memory chunk starts at `ptr` with size `oldsize` /// and is turned into a memory region starting at the same address but with `newsize` bytes. /// Returns `true` iff the access memory region could be freed. fn free_part(&self, ptr: *mut u8, oldsize: usize, newsize: usize) -> bool; /// Frees an entire memory region. Returns `true` iff the operation succeeded. When `false` is /// returned, the `dlmalloc` may re-use the location on future allocation requests fn free(&self, ptr: *mut u8, size: usize) -> bool; /// Indicates if the system can release a part of memory. For the `flags` argument, see /// `Allocator::alloc` fn can_release_part(&self, flags: u32) -> bool; /// Indicates whether newly allocated regions contain zeros. fn allocates_zeros(&self) -> bool; /// Returns the page size. Must be a power of two fn page_size(&self) -> usize; } /// An allocator instance /// /// Instances of this type are used to allocate blocks of memory. For best /// results only use one of these. Currently doesn't implement `Drop` to release /// lingering memory back to the OS. That may happen eventually though! pub struct Dlmalloc(dlmalloc::Dlmalloc); #[cfg(target_family = "wasm")] #[path = "wasm.rs"] mod sys; #[cfg(any(target_os = "linux", target_os = "macos"))] #[path = "unix.rs"] mod sys; #[cfg(target_os = "xous")] #[path = "xous.rs"] mod sys; #[cfg(not(any( target_os = "linux", target_os = "macos", target_os = "xous", target_family = "wasm" )))] #[path = "dummy.rs"] mod sys; impl Dlmalloc { /// Creates a new instance of an allocator pub const fn new() -> Dlmalloc { Dlmalloc(dlmalloc::Dlmalloc::new(System::new())) } } impl Dlmalloc { /// Creates a new instance of an allocator pub const fn new_with_allocator(sys_allocator: A) -> Dlmalloc { Dlmalloc(dlmalloc::Dlmalloc::new(sys_allocator)) } } impl Dlmalloc { /// Allocates `size` bytes with `align` align. /// /// Returns a null pointer if allocation fails. Returns a valid pointer /// otherwise. /// /// Safety and contracts are largely governed by the `GlobalAlloc::alloc` /// method contracts. #[inline] pub unsafe fn malloc(&mut self, size: usize, align: usize) -> *mut u8 { if align <= self.0.malloc_alignment() { self.0.malloc(size) } else { self.0.memalign(align, size) } } /// Same as `malloc`, except if the allocation succeeds it's guaranteed to /// point to `size` bytes of zeros. #[inline] pub unsafe fn calloc(&mut self, size: usize, align: usize) -> *mut u8 { let ptr = self.malloc(size, align); if !ptr.is_null() && self.0.calloc_must_clear(ptr) { ptr::write_bytes(ptr, 0, size); } ptr } /// Deallocates a `ptr` with `size` and `align` as the previous request used /// to allocate it. /// /// Safety and contracts are largely governed by the `GlobalAlloc::dealloc` /// method contracts. #[inline] pub unsafe fn free(&mut self, ptr: *mut u8, size: usize, align: usize) { drop((size, align)); self.0.free(ptr) } /// Reallocates `ptr`, a previous allocation with `old_size` and /// `old_align`, to have `new_size` and the same alignment as before. /// /// Returns a null pointer if the memory couldn't be reallocated, but `ptr` /// is still valid. Returns a valid pointer and frees `ptr` if the request /// is satisfied. /// /// Safety and contracts are largely governed by the `GlobalAlloc::realloc` /// method contracts. #[inline] pub unsafe fn realloc( &mut self, ptr: *mut u8, old_size: usize, old_align: usize, new_size: usize, ) -> *mut u8 { if old_align <= self.0.malloc_alignment() { self.0.realloc(ptr, new_size) } else { let res = self.malloc(new_size, old_align); if !res.is_null() { let size = cmp::min(old_size, new_size); ptr::copy_nonoverlapping(ptr, res, size); self.free(ptr, old_size, old_align); } res } } }