#[cfg(not(target_arch = "riscv64"))]
use core::mem::align_of;
use core::mem::size_of;
use core::ptr::{copy_nonoverlapping, null_mut, write_bytes};
use errno::{set_errno, Errno};
use libc::{c_int, c_void, size_t};
#[cfg(not(any(
feature = "malloc-via-rust-global-alloc",
feature = "malloc-via-crates"
)))]
compile_error!("One of the malloc implementation features must be enabled.");
#[cfg(feature = "malloc-via-rust-global-alloc")]
use alloc::alloc::{alloc as the_alloc, dealloc as the_dealloc};
#[cfg(feature = "malloc-via-crates")]
unsafe fn the_alloc(layout: alloc::alloc::Layout) -> *mut u8 {
core::alloc::GlobalAlloc::alloc(&rustix_dlmalloc::GlobalDlmalloc, layout)
}
#[cfg(feature = "malloc-via-crates")]
unsafe fn the_dealloc(ptr: *mut u8, layout: alloc::alloc::Layout) {
core::alloc::GlobalAlloc::dealloc(&rustix_dlmalloc::GlobalDlmalloc, ptr, layout)
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
struct Tag {
size: usize,
align: usize,
}
fn tagged_alloc(type_layout: alloc::alloc::Layout) -> *mut u8 {
if type_layout.size() == 0 {
return null_mut();
}
let tag_layout = alloc::alloc::Layout::new::<Tag>();
let tag = Tag {
size: type_layout.size(),
align: type_layout.align(),
};
if let Ok((total_layout, offset)) = tag_layout.extend(type_layout) {
let total_ptr = unsafe { the_alloc(total_layout) };
if total_ptr.is_null() {
return total_ptr;
}
let tag_offset = offset - tag_layout.size();
unsafe {
total_ptr.wrapping_add(tag_offset).cast::<Tag>().write(tag);
total_ptr.wrapping_add(offset).cast()
}
} else {
null_mut()
}
}
unsafe fn get_layout(ptr: *mut u8) -> alloc::alloc::Layout {
let tag = ptr.wrapping_sub(size_of::<Tag>()).cast::<Tag>().read();
alloc::alloc::Layout::from_size_align_unchecked(tag.size, tag.align)
}
unsafe fn tagged_dealloc(ptr: *mut u8) {
let tag_layout = alloc::alloc::Layout::new::<Tag>();
let type_layout = get_layout(ptr);
if let Ok((total_layout, offset)) = tag_layout.extend(type_layout) {
let total_ptr = ptr.wrapping_sub(offset);
the_dealloc(total_ptr, total_layout);
}
}
#[linkage = "weak"]
#[no_mangle]
unsafe extern "C" fn malloc(size: usize) -> *mut c_void {
libc!(libc::malloc(size));
let size = if size == 0 { size + 1 } else { size };
#[cfg(target_arch = "riscv64")]
let layout = alloc::alloc::Layout::from_size_align(size, 16);
#[cfg(not(target_arch = "riscv64"))]
let layout = alloc::alloc::Layout::from_size_align(size, align_of::<libc::max_align_t>());
let layout = match layout {
Ok(layout) => layout,
Err(_) => {
set_errno(Errno(libc::ENOMEM));
return null_mut();
}
};
let ret = tagged_alloc(layout);
if ret.is_null() {
set_errno(Errno(libc::ENOMEM));
}
ret.cast()
}
#[linkage = "weak"]
#[no_mangle]
unsafe extern "C" fn realloc(old: *mut c_void, size: usize) -> *mut c_void {
libc!(libc::realloc(old, size));
if old.is_null() {
malloc(size)
} else {
let old_layout = get_layout(old.cast());
if old_layout.size() >= size {
return old;
}
let new = malloc(size);
if !new.is_null() {
copy_nonoverlapping(
old.cast::<u8>(),
new.cast::<u8>(),
core::cmp::min(size, old_layout.size()),
);
}
tagged_dealloc(old.cast());
new
}
}
#[no_mangle]
unsafe extern "C" fn reallocarray(old: *mut c_void, nmemb: size_t, size: size_t) -> *mut c_void {
libc!(libc::reallocarray(old, nmemb, size));
let product = match nmemb.checked_mul(size) {
Some(product) => product,
None => {
set_errno(Errno(libc::ENOMEM));
return null_mut();
}
};
realloc(old, product)
}
#[linkage = "weak"]
#[no_mangle]
unsafe extern "C" fn calloc(nmemb: usize, size: usize) -> *mut c_void {
libc!(libc::calloc(nmemb, size));
let product = match nmemb.checked_mul(size) {
Some(product) => product,
None => {
set_errno(Errno(libc::ENOMEM));
return null_mut();
}
};
let ptr = malloc(product);
write_bytes(ptr, 0, product);
ptr
}
#[no_mangle]
unsafe extern "C" fn posix_memalign(
memptr: *mut *mut c_void,
alignment: usize,
size: usize,
) -> c_int {
libc!(libc::posix_memalign(memptr, alignment, size));
if !alignment.is_power_of_two() || alignment < core::mem::size_of::<*const c_void>() {
return libc::EINVAL;
}
let layout = alloc::alloc::Layout::from_size_align(size, alignment);
let layout = match layout {
Ok(layout) => layout,
Err(_) => return libc::ENOMEM,
};
let ptr = tagged_alloc(layout);
if ptr.is_null() {
return libc::ENOMEM;
}
*memptr = ptr.cast();
0
}
#[deprecated]
#[no_mangle]
unsafe extern "C" fn memalign(alignment: usize, size: usize) -> *mut c_void {
libc!(libc::memalign(alignment, size));
let layout = alloc::alloc::Layout::from_size_align(size, alignment);
let layout = match layout {
Ok(layout) => layout,
Err(_) => {
set_errno(Errno(libc::ENOMEM));
return null_mut();
}
};
let ptr = tagged_alloc(layout);
if ptr.is_null() {
set_errno(Errno(libc::ENOMEM));
return null_mut();
}
ptr.cast()
}
#[linkage = "weak"]
#[no_mangle]
unsafe extern "C" fn aligned_alloc(alignment: size_t, size: size_t) -> *mut c_void {
if !alignment.is_power_of_two() || size % alignment != 0 {
set_errno(Errno(libc::EINVAL));
return null_mut();
}
let layout = alloc::alloc::Layout::from_size_align(size, alignment).unwrap();
let ptr = tagged_alloc(layout);
if ptr.is_null() {
set_errno(Errno(libc::ENOMEM));
return null_mut();
}
ptr.cast()
}
#[deprecated]
#[allow(deprecated)]
#[no_mangle]
unsafe extern "C" fn valloc(size: size_t) -> *mut c_void {
memalign(rustix::param::page_size(), size)
}
#[linkage = "weak"]
#[no_mangle]
unsafe extern "C" fn free(ptr: *mut c_void) {
libc!(libc::free(ptr));
if ptr.is_null() {
return;
}
tagged_dealloc(ptr.cast());
}
#[no_mangle]
unsafe extern "C" fn malloc_usable_size(ptr: *mut c_void) -> size_t {
libc!(libc::malloc_usable_size(ptr));
if ptr.is_null() {
return 0;
}
get_layout(ptr.cast()).size()
}