use crate::*;
use crate::meta::*;
use super::Error;
use winapi::um::heapapi::{HeapAlloc, HeapReAlloc, HeapFree, HeapSize, 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 HeapNoSerialize(HANDLE);
impl Drop for HeapNoSerialize {
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);
}
}
}
#[allow(dead_code)] struct AssertNotSendSync;
impl HeapNoSerialize {
#[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_GENERATE_EXCEPTIONS == 0, "bug: undefined behavior: HeapNoSerialize::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:?}"))
}
}
impl Meta for HeapNoSerialize {
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 HeapNoSerialize {}
#[doc = include_str!("_refs.md")]
unsafe impl thin::Alloc for HeapNoSerialize {
fn alloc_uninit(&self, size: usize) -> Result<AllocNN, Self::Error> {
let alloc = unsafe { HeapAlloc(self.0, HEAP_NO_SERIALIZE, 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_NO_SERIALIZE | 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 HeapNoSerialize {
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, HEAP_NO_SERIALIZE, 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_NO_SERIALIZE | 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 HeapNoSerialize {
unsafe fn free_nullable(&self, ptr: *mut MaybeUninit<u8>) {
if unsafe { HeapFree(self.0, HEAP_NO_SERIALIZE, ptr.cast()) } == 0 && cfg!(debug_assertions) { bug::ub::free_failed(ptr) }
}
}
unsafe impl thin::SizeOf for HeapNoSerialize {}
#[doc = include_str!("_refs.md")]
#[allow(clippy::missing_safety_doc)]
unsafe impl thin::SizeOfDebug for HeapNoSerialize {
unsafe fn size_of_debug(&self, ptr: AllocNN) -> Option<usize> {
let size = unsafe { HeapSize(self.0, HEAP_NO_SERIALIZE, ptr.as_ptr().cast()) };
if size == !0 { return None }
Some(size)
}
}
#[no_implicit_prelude] mod cleanroom {
use super::{impls, HeapNoSerialize};
impls! {
unsafe impl ialloc::fat::Alloc for HeapNoSerialize => ialloc::thin::Alloc;
unsafe impl ialloc::fat::Realloc for HeapNoSerialize => ialloc::thin::Realloc;
unsafe impl ialloc::fat::Free for HeapNoSerialize => ialloc::thin::Free;
}
}
#[cfg(test)] fn create_test_heap(initial_size: Option<NonZeroUsize>, maximum_size: Option<NonZeroUsize>) -> HeapNoSerialize {
unsafe { HeapNoSerialize::create(HEAP_NO_SERIALIZE, initial_size, maximum_size) }
}
#[test] fn thin_alignment() {
thin::test::alignment(create_test_heap(None, None));
thin::test::alignment(create_test_heap(None, NonZeroUsize::new(1024 * 1024)));
}
#[test] fn thin_edge_case_sizes() {
thin::test::edge_case_sizes(create_test_heap(None, None));
thin::test::edge_case_sizes(create_test_heap(None, NonZeroUsize::new(1024 * 1024)));
}
#[test] fn thin_nullable() {
thin::test::nullable(create_test_heap(None, None));
thin::test::nullable(create_test_heap(None, NonZeroUsize::new(1024 * 1024)));
}
#[test] fn thin_size() {
thin::test::size_exact_alloc(create_test_heap(None, None));
thin::test::size_exact_alloc(create_test_heap(None, NonZeroUsize::new(1024 * 1024)));
}
#[test] fn thin_uninit() {
unsafe {
thin::test::uninit_alloc_unsound(create_test_heap(None, None));
thin::test::uninit_alloc_unsound(create_test_heap(None, NonZeroUsize::new(1024 * 1024)));
}
}
#[test] fn thin_uninit_realloc() {
thin::test::uninit_realloc(create_test_heap(None, None));
thin::test::uninit_realloc(create_test_heap(None, NonZeroUsize::new(1024 * 1024)));
}
#[test] fn thin_zeroed() {
thin::test::zeroed_alloc(create_test_heap(None, None));
thin::test::zeroed_alloc(create_test_heap(None, NonZeroUsize::new(1024 * 1024)));
}
#[test] fn thin_zeroed_realloc() {
thin::test::zeroed_realloc(create_test_heap(None, None));
thin::test::zeroed_realloc(create_test_heap(None, NonZeroUsize::new(1024 * 1024)));
}
#[test] fn thin_zst_support() {
thin::test::zst_supported_accurate(create_test_heap(None, None));
thin::test::zst_supported_accurate(create_test_heap(None, NonZeroUsize::new(1024 * 1024)));
}
#[test] fn fat_alignment() {
fat::test::alignment(create_test_heap(None, None));
fat::test::alignment(create_test_heap(None, NonZeroUsize::new(1024 * 1024)));
}
#[test] fn fat_edge_case_sizes() {
fat::test::edge_case_sizes(create_test_heap(None, None));
fat::test::edge_case_sizes(create_test_heap(None, NonZeroUsize::new(1024 * 1024)));
}
#[test] fn fat_uninit() {
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))) };
}
#[test] fn fat_uninit_realloc() {
fat::test::uninit_realloc(create_test_heap(None, None));
fat::test::uninit_realloc(create_test_heap(None, NonZeroUsize::new(1024 * 1024)));
}
#[test] fn fat_zeroed() {
fat::test::zeroed_alloc(create_test_heap(None, None));
fat::test::zeroed_alloc(create_test_heap(None, NonZeroUsize::new(1024 * 1024)));
}
#[test] fn fat_zeroed_realloc() {
fat::test::zeroed_realloc(create_test_heap(None, None));
fat::test::zeroed_realloc(create_test_heap(None, NonZeroUsize::new(1024 * 1024)));
}
#[test] fn fat_zst_support() {
fat::test::zst_supported_accurate(create_test_heap(None, None));
fat::test::zst_supported_accurate(create_test_heap(None, NonZeroUsize::new(1024 * 1024)));
}