#![cfg_attr(feature = "alloc_trait", feature(allocator_api))]
#![deny(missing_docs, broken_intra_doc_links)]
#![no_std]
#[cfg(feature = "alloc_trait")]
use core::alloc::{Alloc, AllocErr, CannotReallocInPlace, Excess};
use core::alloc::{GlobalAlloc, Layout};
#[cfg(feature = "alloc_trait")]
use core::ptr::NonNull;
use libc::{c_int, c_void};
#[cfg(all(any(
target_arch = "arm",
target_arch = "mips",
target_arch = "mipsel",
target_arch = "powerpc"
)))]
const ALIGNOF_MAX_ALIGN_T: usize = 8;
#[cfg(all(any(
target_arch = "x86",
target_arch = "x86_64",
target_arch = "aarch64",
target_arch = "powerpc64",
target_arch = "powerpc64le",
target_arch = "mips64",
target_arch = "s390x",
target_arch = "sparc64"
)))]
const ALIGNOF_MAX_ALIGN_T: usize = 16;
fn layout_to_flags(align: usize, size: usize) -> c_int {
if align <= ALIGNOF_MAX_ALIGN_T && align <= size {
0
} else {
ffi::MALLOCX_ALIGN(align)
}
}
macro_rules! assume {
($e:expr) => {
debug_assert!($e);
if !($e) {
core::hint::unreachable_unchecked();
}
};
}
#[derive(Copy, Clone, Default, Debug)]
pub struct Jemalloc;
unsafe impl GlobalAlloc for Jemalloc {
#[inline]
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
assume!(layout.size() != 0);
let flags = layout_to_flags(layout.align(), layout.size());
let ptr = if flags == 0 {
ffi::malloc(layout.size())
} else {
ffi::mallocx(layout.size(), flags)
};
ptr as *mut u8
}
#[inline]
unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
assume!(layout.size() != 0);
let flags = layout_to_flags(layout.align(), layout.size());
let ptr = if flags == 0 {
ffi::calloc(1, layout.size())
} else {
ffi::mallocx(layout.size(), flags | ffi::MALLOCX_ZERO)
};
ptr as *mut u8
}
#[inline]
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
assume!(!ptr.is_null());
assume!(layout.size() != 0);
let flags = layout_to_flags(layout.align(), layout.size());
ffi::sdallocx(ptr as *mut c_void, layout.size(), flags)
}
#[inline]
unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
assume!(layout.size() != 0);
assume!(new_size != 0);
let flags = layout_to_flags(layout.align(), new_size);
let ptr = if flags == 0 {
ffi::realloc(ptr as *mut c_void, new_size)
} else {
ffi::rallocx(ptr as *mut c_void, new_size, flags)
};
ptr as *mut u8
}
}
#[cfg(feature = "alloc_trait")]
unsafe impl Alloc for Jemalloc {
#[inline]
unsafe fn alloc(&mut self, layout: Layout) -> Result<NonNull<u8>, AllocErr> {
NonNull::new(GlobalAlloc::alloc(self, layout)).ok_or(AllocErr)
}
#[inline]
unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result<NonNull<u8>, AllocErr> {
NonNull::new(GlobalAlloc::alloc_zeroed(self, layout)).ok_or(AllocErr)
}
#[inline]
unsafe fn dealloc(&mut self, ptr: NonNull<u8>, layout: Layout) {
GlobalAlloc::dealloc(self, ptr.as_ptr(), layout)
}
#[inline]
unsafe fn realloc(
&mut self,
ptr: NonNull<u8>,
layout: Layout,
new_size: usize,
) -> Result<NonNull<u8>, AllocErr> {
NonNull::new(GlobalAlloc::realloc(self, ptr.as_ptr(), layout, new_size)).ok_or(AllocErr)
}
#[inline]
unsafe fn alloc_excess(&mut self, layout: Layout) -> Result<Excess, AllocErr> {
let flags = layout_to_flags(layout.align(), layout.size());
let ptr = ffi::mallocx(layout.size(), flags);
if let Some(nonnull) = NonNull::new(ptr as *mut u8) {
let excess = ffi::nallocx(layout.size(), flags);
Ok(Excess(nonnull, excess))
} else {
Err(AllocErr)
}
}
#[inline]
unsafe fn realloc_excess(
&mut self,
ptr: NonNull<u8>,
layout: Layout,
new_size: usize,
) -> Result<Excess, AllocErr> {
let flags = layout_to_flags(layout.align(), new_size);
let ptr = ffi::rallocx(ptr.cast().as_ptr(), new_size, flags);
if let Some(nonnull) = NonNull::new(ptr as *mut u8) {
let excess = ffi::nallocx(new_size, flags);
Ok(Excess(nonnull, excess))
} else {
Err(AllocErr)
}
}
#[inline]
fn usable_size(&self, layout: &Layout) -> (usize, usize) {
let flags = layout_to_flags(layout.align(), layout.size());
unsafe {
let max = ffi::nallocx(layout.size(), flags);
(layout.size(), max)
}
}
#[inline]
unsafe fn grow_in_place(
&mut self,
ptr: NonNull<u8>,
layout: Layout,
new_size: usize,
) -> Result<(), CannotReallocInPlace> {
let flags = layout_to_flags(layout.align(), new_size);
let usable_size = ffi::xallocx(ptr.cast().as_ptr(), new_size, 0, flags);
if usable_size >= new_size {
Ok(())
} else {
Err(CannotReallocInPlace)
}
}
#[inline]
unsafe fn shrink_in_place(
&mut self,
ptr: NonNull<u8>,
layout: Layout,
new_size: usize,
) -> Result<(), CannotReallocInPlace> {
if new_size == layout.size() {
return Ok(());
}
let flags = layout_to_flags(layout.align(), new_size);
let usable_size = ffi::xallocx(ptr.cast().as_ptr(), new_size, 0, flags);
if usable_size < layout.size() {
Ok(())
} else if usable_size == ffi::nallocx(new_size, flags) {
debug_assert_eq!(
ffi::nallocx(new_size, flags),
ffi::nallocx(layout.size(), flags)
);
Ok(())
} else {
Err(CannotReallocInPlace)
}
}
}
pub unsafe fn usable_size<T>(ptr: *const T) -> usize {
ffi::malloc_usable_size(ptr as *const c_void)
}
mod ffi {
pub use tikv_jemalloc_sys::*;
}