#![deny(unsafe_op_in_unsafe_fn)]
use crate::alloc::{GlobalAlloc, Layout, System};
use crate::ffi::c_void;
use crate::ptr;
use crate::sync::atomic::{AtomicPtr, Ordering};
use crate::sys::c;
use crate::sys::common::alloc::{realloc_fallback, MIN_ALIGN};
#[cfg(test)]
mod tests;
const HEAP_ZERO_MEMORY: c::DWORD = 0x00000008;
#[link(name = "kernel32")]
extern "system" {
fn GetProcessHeap() -> c::HANDLE;
fn HeapAlloc(hHeap: c::HANDLE, dwFlags: c::DWORD, dwBytes: c::SIZE_T) -> c::LPVOID;
fn HeapReAlloc(
hHeap: c::HANDLE,
dwFlags: c::DWORD,
lpMem: c::LPVOID,
dwBytes: c::SIZE_T,
) -> c::LPVOID;
fn HeapFree(hHeap: c::HANDLE, dwFlags: c::DWORD, lpMem: c::LPVOID) -> c::BOOL;
}
static HEAP: AtomicPtr<c_void> = AtomicPtr::new(ptr::null_mut());
#[inline]
fn init_or_get_process_heap() -> c::HANDLE {
let heap = HEAP.load(Ordering::Relaxed);
if heap.is_null() {
let heap = unsafe { GetProcessHeap() };
if !heap.is_null() {
HEAP.store(heap, Ordering::Release);
heap
} else {
ptr::null_mut()
}
} else {
heap
}
}
#[inline]
unsafe fn get_process_heap() -> c::HANDLE {
HEAP.load(Ordering::Acquire)
}
#[repr(C)]
struct Header(*mut u8);
#[inline]
unsafe fn allocate(layout: Layout, zeroed: bool) -> *mut u8 {
let heap = init_or_get_process_heap();
if heap.is_null() {
return ptr::null_mut();
}
let flags = if zeroed { HEAP_ZERO_MEMORY } else { 0 };
if layout.align() <= MIN_ALIGN {
unsafe { HeapAlloc(heap, flags, layout.size()) as *mut u8 }
} else {
let total = layout.align() + layout.size();
let ptr = unsafe { HeapAlloc(heap, flags, total) as *mut u8 };
if ptr.is_null() {
return ptr::null_mut();
}
let offset = layout.align() - (ptr.addr() & (layout.align() - 1));
let aligned = unsafe { ptr.add(offset) };
unsafe { ptr::write((aligned as *mut Header).sub(1), Header(ptr)) };
aligned
}
}
#[stable(feature = "alloc_system_type", since = "1.28.0")]
unsafe impl GlobalAlloc for System {
#[inline]
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
let zeroed = false;
unsafe { allocate(layout, zeroed) }
}
#[inline]
unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
let zeroed = true;
unsafe { allocate(layout, zeroed) }
}
#[inline]
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
let block = {
if layout.align() <= MIN_ALIGN {
ptr
} else {
unsafe { ptr::read((ptr as *mut Header).sub(1)).0 }
}
};
let heap = unsafe { get_process_heap() };
unsafe { HeapFree(heap, 0, block as c::LPVOID) };
}
#[inline]
unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
if layout.align() <= MIN_ALIGN {
let heap = unsafe { get_process_heap() };
unsafe { HeapReAlloc(heap, 0, ptr as c::LPVOID, new_size) as *mut u8 }
} else {
unsafe { realloc_fallback(self, ptr, layout, new_size) }
}
}
}