blob: 672618b2ff115fa70c06652cc9f7e9cbb9847ba1 [file] [log] [blame]
//! 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.
#![cfg_attr(feature = "allocator-api", feature(allocator_api))]
#![cfg_attr(target_env = "sgx", feature(asm))]
#![cfg_attr(not(feature = "allocator-api"), allow(dead_code))]
#![no_std]
#![deny(missing_docs)]
#[cfg(feature = "allocator-api")]
use core::alloc::{Alloc, Layout, AllocErr};
use core::cmp;
use core::ptr;
#[cfg(all(feature = "global", not(test)))]
pub use self::global::GlobalDlmalloc;
#[cfg(all(feature = "global", not(test)))]
mod global;
mod dlmalloc;
/// 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);
/// Constant initializer for `Dlmalloc` structure.
pub const DLMALLOC_INIT: Dlmalloc = Dlmalloc(dlmalloc::DLMALLOC_INIT);
#[cfg(target_arch = "wasm32")]
#[path = "wasm.rs"]
mod sys;
#[cfg(target_os = "macos")]
#[path = "macos.rs"]
mod sys;
#[cfg(target_os = "linux")]
#[path = "linux.rs"]
mod sys;
#[cfg(target_env = "sgx")]
#[path = "sgx.rs"]
mod sys;
impl Dlmalloc {
/// Creates a new instance of an allocator, same as `DLMALLOC_INIT`.
pub fn new() -> Dlmalloc {
DLMALLOC_INIT
}
/// 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
}
}
}
#[cfg(feature = "allocator-api")]
unsafe impl Alloc for Dlmalloc {
#[inline]
unsafe fn alloc(
&mut self,
layout: Layout
) -> Result<ptr::NonNull<u8>, AllocErr> {
let ptr = <Dlmalloc>::malloc(self, layout.size(), layout.align());
ptr::NonNull::new(ptr).ok_or(AllocErr)
}
#[inline]
unsafe fn dealloc(&mut self, ptr: ptr::NonNull<u8>, layout: Layout) {
<Dlmalloc>::free(self, ptr.as_ptr(), layout.size(), layout.align())
}
#[inline]
unsafe fn realloc(
&mut self,
ptr: ptr::NonNull<u8>,
layout: Layout,
new_size: usize
) -> Result<ptr::NonNull<u8>, AllocErr> {
let ptr = <Dlmalloc>::realloc(
self,
ptr.as_ptr(),
layout.size(),
layout.align(),
new_size,
);
ptr::NonNull::new(ptr).ok_or(AllocErr)
}
#[inline]
unsafe fn alloc_zeroed(
&mut self,
layout: Layout
) -> Result<ptr::NonNull<u8>, AllocErr> {
let ptr = <Dlmalloc>::calloc(self, layout.size(), layout.align());
ptr::NonNull::new(ptr).ok_or(AllocErr)
}
}