use crate::*;
use crate::meta::*;
use super::Error;
use winapi::um::heapapi::{HeapAlloc, HeapReAlloc, HeapFree, HeapSize, GetProcessHeap, HeapDestroy, HeapCreate};
use winapi::um::winnt::{HANDLE, HEAP_ZERO_MEMORY, HEAP_NO_SERIALIZE, HEAP_GENERATE_EXCEPTIONS};
use core::mem::MaybeUninit;
use core::num::NonZeroUsize;
use core::ptr::NonNull;
#[doc = include_str!("_refs.md")]
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(transparent)] pub struct Heap(HANDLE);
impl From<ProcessHeap> for &'static Heap { fn from(_: ProcessHeap) -> Self { Heap::process() } }
impl Drop for Heap {
fn drop(&mut self) {
let succeeds = unsafe { HeapDestroy(self.0) };
if succeeds == 0 {
let err = super::get_last_error();
panic!("HeapDestroy({:?}) failed with GetLastError() == 0x{:08x}", self.0, err);
}
}
}
unsafe impl Sync for Heap {}
unsafe impl Send for Heap {}
#[test] fn cross_thread_destroy_fuzz_test() {
let threads = (0..50).map(|_| {
let heap = create_test_heap(None, None);
thin::test::alignment(&heap); std::thread::spawn(move || std::mem::drop(heap)) }).collect::<std::vec::Vec<_>>();
for thread in threads { thread.join().unwrap() }
}
impl Heap {
#[doc = include_str!("_refs.md")]
pub unsafe fn borrow(handle: &HANDLE) -> &Self {
unsafe { core::mem::transmute(handle) }
}
#[doc = include_str!("_refs.md")]
pub unsafe fn try_create(options: u32, initial_size: Option<NonZeroUsize>, maximum_size: Option<NonZeroUsize>) -> Result<Self, Error> {
assert!(options & HEAP_NO_SERIALIZE == 0, "bug: undefined behavior: Heap::try_create cannot be used with HEAP_NO_SERIALIZE");
assert!(options & HEAP_GENERATE_EXCEPTIONS == 0, "bug: undefined behavior: Heap::try_create cannot be used with HEAP_GENERATE_EXCEPTIONS");
let initial_size = initial_size.map_or(0, |nz| nz.get());
let maximum_size = maximum_size.map_or(0, |nz| nz.get());
let handle = unsafe { HeapCreate(options, initial_size, maximum_size) };
if handle.is_null() { return Err(Error::get_last()) }
Ok(Self(handle))
}
#[doc = include_str!("_refs.md")]
pub unsafe fn create(options: u32, initial_size: Option<NonZeroUsize>, maximum_size: Option<NonZeroUsize>) -> Self {
unsafe { Self::try_create(options, initial_size, maximum_size) }.unwrap_or_else(|err| panic!("HeapCreate failed with GetLastError() == {err:?}"))
}
#[doc = include_str!("_refs.md")]
pub fn process() -> &'static Self {
lazy_static::lazy_static! { static ref PROCESS_HEAP : ThreadSafeHandle = ThreadSafeHandle(unsafe { GetProcessHeap() }); }
struct ThreadSafeHandle(HANDLE);
unsafe impl Send for ThreadSafeHandle {}
unsafe impl Sync for ThreadSafeHandle {}
let handle = &(PROCESS_HEAP.0);
debug_assert!(!(*handle).is_null(), "GetProcessHeap() returned nullptr");
unsafe { Self::borrow(handle) }
}
#[doc = include_str!("_refs.md")]
pub fn with_process<R>(f: impl FnOnce(&Self) -> R) -> R {
f(Self::process())
}
}
impl Meta for Heap {
type Error = Error;
const MIN_ALIGN : Alignment = super::MEMORY_ALLOCATION_ALIGNMENT;
const MAX_ALIGN : Alignment = super::MEMORY_ALLOCATION_ALIGNMENT; const MAX_SIZE : usize = usize::MAX;
const ZST_SUPPORTED : bool = true;
}
impl ZstSupported for Heap {}
#[doc = include_str!("_refs.md")]
unsafe impl thin::Alloc for Heap {
fn alloc_uninit(&self, size: usize) -> Result<AllocNN, Self::Error> {
let alloc = unsafe { HeapAlloc(self.0, 0, size) };
NonNull::new(alloc.cast()).ok_or_else(Error::get_last)
}
fn alloc_zeroed(&self, size: usize) -> Result<AllocNN0, Self::Error> {
let alloc = unsafe { HeapAlloc(self.0, HEAP_ZERO_MEMORY, size) };
NonNull::new(alloc.cast()).ok_or_else(Error::get_last)
}
}
#[doc = include_str!("_refs.md")]
#[allow(clippy::missing_safety_doc)]
unsafe impl thin::Realloc for Heap {
const CAN_REALLOC_ZEROED : bool = true;
unsafe fn realloc_uninit(&self, ptr: AllocNN, new_size: usize) -> Result<AllocNN, Self::Error> {
let alloc = unsafe { HeapReAlloc(self.0, 0, ptr.as_ptr().cast(), new_size) };
NonNull::new(alloc.cast()).ok_or_else(Error::get_last)
}
unsafe fn realloc_zeroed(&self, ptr: AllocNN, new_size: usize) -> Result<AllocNN, Self::Error> {
let alloc = unsafe { HeapReAlloc(self.0, HEAP_ZERO_MEMORY, ptr.as_ptr().cast(), new_size) };
NonNull::new(alloc.cast()).ok_or_else(Error::get_last)
}
}
#[doc = include_str!("_refs.md")]
#[allow(clippy::missing_safety_doc)]
unsafe impl thin::Free for Heap {
unsafe fn free_nullable(&self, ptr: *mut MaybeUninit<u8>) {
if unsafe { HeapFree(self.0, 0, ptr.cast()) } == 0 && cfg!(debug_assertions) { bug::ub::free_failed(ptr) }
}
}
unsafe impl thin::SizeOf for Heap {}
#[doc = include_str!("_refs.md")]
#[allow(clippy::missing_safety_doc)]
unsafe impl thin::SizeOfDebug for Heap {
unsafe fn size_of_debug(&self, ptr: AllocNN) -> Option<usize> {
let size = unsafe { HeapSize(self.0, 0, ptr.as_ptr().cast()) };
if size == !0 { return None }
Some(size)
}
}
#[doc = include_str!("_refs.md")]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(transparent)] pub struct ProcessHeap;
impl Meta for ProcessHeap {
type Error = Error;
const MIN_ALIGN : Alignment = super::MEMORY_ALLOCATION_ALIGNMENT;
const MAX_ALIGN : Alignment = super::MEMORY_ALLOCATION_ALIGNMENT; const MAX_SIZE : usize = usize::MAX;
const ZST_SUPPORTED : bool = true;
}
impl ZstSupported for ProcessHeap {}
unsafe impl Stateless for ProcessHeap {}
#[allow(clippy::undocumented_unsafe_blocks)] unsafe impl thin::Alloc for ProcessHeap {
fn alloc_uninit(&self, size: usize) -> Result<AllocNN, Self::Error> { Heap::with_process(|heap| heap.alloc_uninit(size)) }
fn alloc_zeroed(&self, size: usize) -> Result<AllocNN0, Self::Error> { Heap::with_process(|heap| heap.alloc_zeroed(size)) }
}
#[allow(clippy::undocumented_unsafe_blocks)] unsafe impl thin::Realloc for ProcessHeap {
const CAN_REALLOC_ZEROED : bool = Heap::CAN_REALLOC_ZEROED;
unsafe fn realloc_uninit(&self, ptr: AllocNN, new_size: usize) -> Result<AllocNN, Self::Error> { Heap::with_process(|heap| unsafe { heap.realloc_uninit(ptr, new_size) }) }
unsafe fn realloc_zeroed(&self, ptr: AllocNN, new_size: usize) -> Result<AllocNN, Self::Error> { Heap::with_process(|heap| unsafe { heap.realloc_zeroed(ptr, new_size) }) }
}
#[allow(clippy::undocumented_unsafe_blocks)] unsafe impl thin::Free for ProcessHeap {
unsafe fn free (&self, ptr: NonNull<MaybeUninit<u8>>) { Heap::with_process(|heap| unsafe { heap.free(ptr) }) }
unsafe fn free_nullable(&self, ptr: *mut MaybeUninit<u8> ) { Heap::with_process(|heap| unsafe { heap.free_nullable(ptr) }) }
}
#[allow(clippy::undocumented_unsafe_blocks)] unsafe impl thin::SizeOf for ProcessHeap {}
#[allow(clippy::undocumented_unsafe_blocks)] unsafe impl thin::SizeOfDebug for ProcessHeap { unsafe fn size_of_debug(&self, ptr: AllocNN) -> Option<usize> { Heap::with_process(|heap| unsafe { heap.size_of_debug(ptr) }) } }
#[no_implicit_prelude] mod cleanroom {
use super::{impls, Heap, ProcessHeap};
impls! {
unsafe impl ialloc::fat::Alloc for Heap => ialloc::thin::Alloc;
unsafe impl ialloc::fat::Realloc for Heap => ialloc::thin::Realloc;
unsafe impl ialloc::fat::Free for Heap => ialloc::thin::Free;
unsafe impl ialloc::fat::Alloc for ProcessHeap => ialloc::thin::Alloc;
unsafe impl ialloc::fat::Realloc for ProcessHeap => ialloc::thin::Realloc;
unsafe impl ialloc::fat::Free for ProcessHeap => ialloc::thin::Free;
}
}
#[cfg(test)] fn create_test_heap(initial_size: Option<NonZeroUsize>, maximum_size: Option<NonZeroUsize>) -> Heap {
unsafe { Heap::create(0, initial_size, maximum_size) }
}
#[test] fn thin_alignment() {
thin::test::alignment(ProcessHeap);
thin::test::alignment(create_test_heap(None, None));
thin::test::alignment(create_test_heap(None, NonZeroUsize::new(1024 * 1024)));
Heap::with_process(|heap| thin::test::alignment(heap));
}
#[test] fn thin_edge_case_sizes() {
thin::test::edge_case_sizes(ProcessHeap);
thin::test::edge_case_sizes(create_test_heap(None, None));
thin::test::edge_case_sizes(create_test_heap(None, NonZeroUsize::new(1024 * 1024)));
Heap::with_process(|heap| thin::test::edge_case_sizes(heap));
}
#[test] fn thin_nullable() {
thin::test::nullable(ProcessHeap);
thin::test::nullable(create_test_heap(None, None));
thin::test::nullable(create_test_heap(None, NonZeroUsize::new(1024 * 1024)));
Heap::with_process(|heap| thin::test::nullable(heap));
}
#[test] fn thin_size() {
thin::test::size_exact_alloc(ProcessHeap);
thin::test::size_exact_alloc(create_test_heap(None, None));
thin::test::size_exact_alloc(create_test_heap(None, NonZeroUsize::new(1024 * 1024)));
Heap::with_process(|heap| thin::test::size_exact_alloc(heap));
}
#[test] fn thin_uninit() {
unsafe {
thin::test::uninit_alloc_unsound(ProcessHeap);
thin::test::uninit_alloc_unsound(create_test_heap(None, None));
thin::test::uninit_alloc_unsound(create_test_heap(None, NonZeroUsize::new(1024 * 1024)));
Heap::with_process(|heap| thin::test::uninit_alloc_unsound(heap));
}
}
#[test] fn thin_uninit_realloc() {
thin::test::uninit_realloc(ProcessHeap);
thin::test::uninit_realloc(create_test_heap(None, None));
thin::test::uninit_realloc(create_test_heap(None, NonZeroUsize::new(1024 * 1024)));
Heap::with_process(|heap| thin::test::uninit_realloc(heap));
}
#[test] fn thin_zeroed() {
thin::test::zeroed_alloc(ProcessHeap);
thin::test::zeroed_alloc(create_test_heap(None, None));
thin::test::zeroed_alloc(create_test_heap(None, NonZeroUsize::new(1024 * 1024)));
Heap::with_process(|heap| thin::test::zeroed_alloc(heap));
}
#[test] fn thin_zeroed_realloc() {
thin::test::zeroed_realloc(ProcessHeap);
thin::test::zeroed_realloc(create_test_heap(None, None));
thin::test::zeroed_realloc(create_test_heap(None, NonZeroUsize::new(1024 * 1024)));
Heap::with_process(|heap| thin::test::zeroed_realloc(heap));
}
#[test] fn thin_zst_support() {
thin::test::zst_supported_accurate(ProcessHeap);
thin::test::zst_supported_accurate(create_test_heap(None, None));
thin::test::zst_supported_accurate(create_test_heap(None, NonZeroUsize::new(1024 * 1024)));
Heap::with_process(|heap| thin::test::zst_supported_accurate(heap));
}
#[test] fn fat_alignment() {
fat::test::alignment(ProcessHeap);
fat::test::alignment(create_test_heap(None, None));
fat::test::alignment(create_test_heap(None, NonZeroUsize::new(1024 * 1024)));
Heap::with_process(|heap| fat::test::alignment(heap));
}
#[test] fn fat_edge_case_sizes() {
fat::test::edge_case_sizes(ProcessHeap);
fat::test::edge_case_sizes(create_test_heap(None, None));
fat::test::edge_case_sizes(create_test_heap(None, NonZeroUsize::new(1024 * 1024)));
Heap::with_process(|heap| fat::test::edge_case_sizes(heap));
}
#[test] fn fat_uninit() {
unsafe { fat::test::uninit_alloc_unsound(ProcessHeap) };
unsafe { fat::test::uninit_alloc_unsound(create_test_heap(None, None)) };
unsafe { fat::test::uninit_alloc_unsound(create_test_heap(None, NonZeroUsize::new(1024 * 1024))) };
Heap::with_process(|heap| unsafe { fat::test::uninit_alloc_unsound(heap) });
}
#[test] fn fat_uninit_realloc() {
fat::test::uninit_realloc(ProcessHeap);
fat::test::uninit_realloc(create_test_heap(None, None));
fat::test::uninit_realloc(create_test_heap(None, NonZeroUsize::new(1024 * 1024)));
Heap::with_process(|heap| fat::test::uninit_realloc(heap));
}
#[test] fn fat_zeroed() {
fat::test::zeroed_alloc(ProcessHeap);
fat::test::zeroed_alloc(create_test_heap(None, None));
fat::test::zeroed_alloc(create_test_heap(None, NonZeroUsize::new(1024 * 1024)));
Heap::with_process(|heap| fat::test::zeroed_alloc(heap));
}
#[test] fn fat_zeroed_realloc() {
fat::test::zeroed_realloc(ProcessHeap);
fat::test::zeroed_realloc(create_test_heap(None, None));
fat::test::zeroed_realloc(create_test_heap(None, NonZeroUsize::new(1024 * 1024)));
Heap::with_process(|heap| fat::test::zeroed_realloc(heap));
}
#[test] fn fat_zst_support() {
fat::test::zst_supported_accurate(ProcessHeap);
fat::test::zst_supported_accurate(create_test_heap(None, None));
fat::test::zst_supported_accurate(create_test_heap(None, NonZeroUsize::new(1024 * 1024)));
Heap::with_process(|heap| fat::test::zst_supported_accurate(heap));
}